NT4/private/ntos/dd/harddisk/atdisk.c
2020-09-30 17:12:29 +02:00

9169 lines
272 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1991, 1992, 1993 Microsoft Corporation
Module Name:
atdisk.c
Abstract:
This is the IBM AT disk (aka ST506) driver for NT.
Author:
Chad Schwitters (chads) 21-Feb-1991.
Environment:
Kernel mode only.
Notes:
The ST506 is the controller standard used on almost all ISA PCs
(Industry Standard Architecture, which use Intel x86 processors).
More advanced controllers, found on either ix86 or MIPS machines,
generally have at least an ST506-compatible mode.
Things that this driver still needs to have done:
Rework to allow for disks with more than 2^32 sectors.
An I/O request passed in by the system is called a "request". This
driver often breaks the request into smaller pieces called "transfers"
to get around limitations and bugs in the controller.
Each request is ordered by starting sector number when placed in the
device queue via IoStartPacket. When removed from the device queue
via IoStartNextPacket, the request at the next higher request is
started - or the lowest request, if there are none higher. This
implements a circular scan of the disk - requests sequentially
higher on the disk are serviced, until we come to the end and start
over again at the beginning of the disk. This functionality is
provided by the I/O system; all the driver has to do is pass in the
sector numbers. The advantage is greater throughput when the disk is
busy, since seek time is reduced. There are faster algorithms, but
they are not as inherently fair as C-SCAN. (Note that this driver
doesn't attempt to work by cylinder numbers; although seek times might
be further reduced, complexity goes up, it might be wrong if the drive
does physical translation, and the possibility of starvation is
introduced).
Background on ST506 operation:
The controller can be reset by
o - writing 4 to the drive control register. This resets the
controller.
o - waiting at least 10us
o - writing 0 to the drive control register. This reenables
the controller and reenables interrupts.
o - issuing a SET PARAMETERS command for each drive attached
to the controller.
Note that the SET PARAMETERS commands each cause an interrupt,
but the actual reset does not. The disk parameters to use in the
SET PARAMETERS command should be supplied by configuration
management.
Commands are issued by writing parameters to relevant registers,
and then writing the command to the COMMAND register. At that
point, the controller generally sets the BUSY bit in its STATUS
register. While this bit is set, the driver can't read or write
any of the registers. Other STATUS register bits are undefined.
An exception is the WRITE command, which sets DRQ and expects
the cache to be filled.
Many controllers are capable of operating in AT PIO, block, and
EISA "B" slave DMA data transfer modes. However, older
controllers don't have block mode and many target machines are
not EISA, so we will only consider AT PIO mode. We would use
block mode where available, but some controllers with block mode
have fatal bugs. We will implement block mode in this driver
once controller manufacturers notify us of a method for
determining which controllers can *correctly* do block mode.
In AT PIO mode, bus transfers are performed by 16-bit I/O space
instructions. An interrupt is generated for each sector (512
bytes, or 256 words) that is transferred.
When a READ is performed, the controller seeks to the correct
track (the seek is implicit, but can also be done explicitly),
reads the sector into an internal cache, sets the DRQ bit and
interrupts the host. As soon as the host transfers 256 words
from the cache (i.e. no action is necessary other than reading
the cache), the controller reads the next sector in the same
fashion as long as there is another sector to be read. If a
non-recoverable error occurs, the sector number register will
indicate which sector was at fault, and the sector count
register will indicate how many sectors weren't transferred.
The WRITE command is the same, except DRQ is set after the
command is issued with no interrupt. This could take up 10 ms
if, for example, the disk has power saving features. The host
should fill the buffer, at which time it is immediately written,
and the interrupt occurs when the write is done.
The controller registers are as follows:
Register name ISA 1st ISA 2nd Access
address address
DATA 0x1F0 0x170 read/write
ERROR 0x1F1 0x171 read only
WRITE PRECOMPENSATION 0x1F1 0x171 write only
SECTOR COUNT 0x1F2 0x172 read/write
SECTOR NUMBER 0x1F3 0x173 read/write
CYLINDER LOW 0x1F4 0x174 read/write
CYLINDER HIGH 0x1F5 0x175 read/write
DRIVE SELECT & HEAD 0x1F6 0x176 read/write
STATUS 0x1F7 0x177 read only
COMMAND 0x1F7 0x177 write only
DRIVE CONTROL 0x3f6 0x376 read/write
The DATA register is the only 16-bit register of the bunch. It
is a window to the sector cache on the controller. Just write
256 words to this port, and the cache will be filled. The
controller can tell when 256 words have been transferred.
The bits of the ERROR register are defined as follows:
0 - data address mark not found after finding ID field
1 - track 0 not found during RECALIBRATE command
2 - command aborted; command invalid or drive status invalid
3 - reserved, 0
4 - requested sector ID field not found
5 - reserved, 0
6 - unrecoverable CRC data error
7 - a bad-block mark was found in the requested sector ID field
These bits are defined only when the error bit is set in the
STATUS register.
The WRITE PRECOMPENSATION register specifies at which cylinder
to start bit recording timeshifts to compensate for an inherent
magnetic recording shift. The value in the register is actually
multiplied by 4 to determine which cylinder is the first to get
precompensation. A value of -1 (all bits set) indicates that
the drive does not use write precompensation.
The SECTOR COUNT register usually indicates how many sectors are
to be read or written. It is the number of sectors per track
for the SET PARAMETERS and FORMAT commands. A value of 0
indicates 256, which is the maximum value.
The SECTOR NUMBER register is programmed with the starting
sector for the command and is automatically updated by the
controller as sectors are transferred in a multi-sector
operation.
The CYLINDER LOW register holds the 8 least significant bits of
the cylinder number.
The CYLINDER HIGH register holds the 3 most significant bits of
the cylinder number in its low-order 3 bits. The high-order
bits are reserved.
The DRIVE SELECT & HEAD register selects a drive, head, and sector
size. The bits are as follows:
0 - 3: choose head 0 through 15
4: 0 = drive 1, 1 = drive 2
5 - 6: always 01, which indicates 512 bytes/sector
7: reserved, always 1
Note that early controllers allowed 4 drives but only 8 heads;
ISA machines all allow this 2 drive/16 head alternate. Early
controllers also allowed a choice of 128-byte, 256-byte, 512-
byte or 1024-byte sectors, but ISA seems to allow only 512.
The bits of the STATUS register are defined as follows:
0 - error (see ERROR register). Next command resets this bit
1 - index - set to 1 each time the disk completes a rotation
2 - data was corrected
3 - data request (DRQ) - sector cache requires service
4 - seek complete
5 - write fault
6 - drive ready
7 - busy (all other bits unreliable; do not issue commands)
This register should be read from an ISR to clear the interrupt
at the controller. Note that an alternate STATUS register,
which is the same but doesn't stop interrupts, is at 0x3f6.
The following are common command values:
SET DRIVE PARAMETERS 0x91
SEEK 0x70
RECALIBRATE 0x10
READ SECTOR 0x20
(0x21 without automatic retries)
WRITE SECTOR 0x30
(0x31 without automatic retries)
FORMAT TRACK 0x50
READ VERIFY 0x40
EXECUTE DIAGNOSTICS 0x90
Many controllers define additional commands; a driver could try
them and then check bit 2 in the ERROR register to see if the
controller supports a particular command.
SET DRIVE PARAMETERS should be issued after every controller
reset. The DRIVE SELECT & HEAD register should have a drive
selected and it should contain the maximum head number. The
SECTOR COUNT register should hold the number of sectors per
track. An interrupt is generated when the command is complete.
SEEK is not necessary since seeks are implicit in the READ and
WRITE commands, but a driver can issue explicit SEEKs and then
operate on the other drive (since the busy bit in the STATUS
register is only set for the first ~35us to ~135us of the SEEK
command). Set the cylinder number in the CYLINDER HIGH and LOW
registers, and select the proper drive and head in the DRIVE
SELECT & HEAD register. An interrupt will be generated when an
explicit SEEK finishes.
RECALIBRATE moves the heads to cylinder 0. The proper drive must
be selected in the DRIVE SELECT & HEAD register. An interrupt
is generated when the command is complete.
READ SECTOR reads the number of sectors specified in the SECTOR
COUNT register, starting with the one specified by the DRIVE
SELECT & HEAD, CYLINDER HIGH & LOW, and SECTOR NUMBER registers.
An interrupt is generated for each sector read.
WRITE SECTOR writes the number of sectors specified in the
SECTOR COUNT register, starting with the one specified by the
DRIVE SELECT & HEAD, CYLINDER HIGH & LOW, and SECTOR NUMBER
registers. An interrupt is generated for each sector written.
The WRITE PRECOMPENSATION register should be set to the proper
cylinder (div 4) for the disk before issuing this command.
Rules on using objects and their variables:
Since we expect this driver to run on a symmetric
multiprocessing system, we must ensure that there are no data
contention problems. Data can be associated with four things:
an IRP, a partition, a device, and a controller. IRP variables
are only modified when a request is made and completed (that is,
before and after I/O, not during). Since the IRPs aren't driver
specific we don't have to worry about contention. Partition
variables are really constants (they are set up during init and
never changed) so they can be read at any time. Device and
controller variables are of both kinds, constants and variable.
The constant values can, of course, be read at any time. So all
we have to worry about are the device and controller "real"
variables. Access to device variables is restricted by the
device queues, and access to controller variables (and
hardware!) is restricted by ownership of the controller object;
control flow is serial so multiple instances of a routine is not
a concern.
A few variables are of special concern because they are modified
by AtDiskInterruptService() and/or AtCheckTimerSync(). For
example InterruptRequiresDpc, InterruptTimer, and
ResettingController. In general, these variables are only
modified by routines that have been called via
KeSynchronizeExecution(). There are some exceptions during
driver initialization and in AtDiskDeferredProcedure(), but
they are well documented (in short, they are safe because we are
sure that AtDiskInterruptService() and AtCheckTimerSync()
aren't going to run while we're modifying the variables).
The device extension is the most-used object - it has variables
to track the I/O progressions. Each disk has a device extension
attached to the device object for partition 0.
Variables in the device extension:
CurrentAddress is a system-space pointer to where we're
currently at in the user's buffer. It is initially passed
in via the MDL, and updated after every copy to/from the
controller cache.
FirstSectorOfRequest is the sector at which the current request
started. It is calculated the first time AtDiskStartIo() is
called for a request. When a request is completed, it is set
to MAXULONG so that we know to set it again for the next request.
The value is passed in to IoStartNextPacketByKey(), so that
the next packet in C-SCAN order will be processed.
FirstSectorOfTransfer is the sector at which the current
transfer started. It is initially calculated in
AtDiskStartIo(), and updated in AtDiskDeferred() just
before calling AtStartDevice() when the controller needs to
be reprogrammed.
RemainingRequestLength is the amount of this request that
still needs to be moved. It starts as the length requested
by the user and, after each interrupt, is decremented by the
number of bytes transferred for that interrupt.
TotalTransferLength is the length of the current transfer
(which might only be a piece of the current request; it is
limited to MAX_SEC_TO_TRANS sectors). It is calculated as
RemainingRequestLength modulo hardware limitations initially
in AtDiskStartIo(), and again in AtDiskDeferred() before
calling AtStartDevice() to start a secondary transfer.
RemainingTransferLength is the amount of the current
transfer that will remain after the current interrupt. It
starts as TotalTransferLength and, after each interrupt, is
decremented by the number of bytes transferred for that
interrupt.
IrpRetryCount is initialized to 0 every time an IRP first
gets to AtDiskStartIo(). If the hardware gets reset and the
packet is restarted via AtDiskStartIo(), PacketIsBeingRetried
will be set to TRUE. AtDiskStartIo() will see this and
increment IrpRetryCount. AtDiskDeferredProcedure() checks
IrpRetryCounu before restarting the packet, and returns the
packet with error if it has reached RETRY_IRP_MAXIMUM_COUNT.
SequenceNumber is a value that is incremented each time
a new irp passes through start io. This is so that if we
need to log an error, we can uniquely identify a particular
irp.
All other partitions have a partition extension attached to
their device object. The partition extension contains only the
information necessary to access and use the proper device
extension. The beginning of the device extension looks like the
beginning of a partition extension, so that the mainline code to
access the device extension will work whether the original
request was based on partition 0 or some other partition.
The controller object has an attached controller extension.
Controller extensions contain variables that are specific to the
controller, rather than to the attached disks.
The driver does not release the controller object until the
controller is finished with an operation. This means that when
the controller object is acquired, the controller can be
programmed immediately.
The controller extension points to the device extensions (those
associated with partition 0) of the disks that are attached to
the controller. If two drives are successfully initialized, the
second one (DRIVE_2 to the controller) is pointed at by Disk2 -
this is the only time that Disk2 is not NULL. Disk1 always
points at the first device successfully initialized, whether
that be DRIVE_1 or DRIVE_2 to the controller.
Variables in the controller extension:
Whenever the code is about to do something that will cause
an interrupt, it starts the timer (via set InterruptTimer =
START_TIMER). This is always cancelled by
AtDiskInterruptService(). Note that "starting" the timer
simply means setting it to 2 (it's called once every second;
but the interval until the end of the current second is
unknown so 2 seconds is the minimum waiting time).
"Cancelling" the timer simply means to set it to -1 so that
the timer routine ignores it. The timer routine will
decrement the counter only if it is nonnegative, and if the
counter ever reaches zero the timer routine will log an
error, reset the controller, and restart the IRP.
Whenever AtDiskInterruptService() should dispatch a DPC,
InterruptRequiresDpc should be set to TRUE at the same time
that InterruptTimer is started. This will be set to FALSE
by AtDiskInterruptService().
WhichDeviceObject should be set to the device object of the
disk that is expecting an interrupt whenever InterruptTimer
is started.
ResettingController is normally RESET_NOT_RESETTING. When
AtCheckTimerSync() determines that the timer has expired, it
sets ResettingController to RESET_FIRST_DRIVE_SET. It then
resets the controller and sets the drive parameters for the
drive that timed out. AtDiskDeferredProcedure() will notice
the state of ResettingController and take the appropriate
action. First, it logs an error; then it recalibrates the
failing drive, changing the state to
RESET_FIRST_DRIVE_RECALIBRATED. If there is only one drive
it will then restart the IRP, set ResettingController to
RESET_NOT_RESETTING and free the controller object. If
there are two drives, it will set ResettingController to
RESET_SECOND_DRIVE_SET and then set the drive parameters of
the second drive. When AtDiskDeferredProcedure() sees
ResettingController in this state, it will restart the IRP
that timed out, set ResettingController to
RESET_NOT_RESETTING, and free the controller object. Note
that the second drive was not recalibrated; we only need to
do this for the failing drive.
Hardware quirks:
The controller's IDENTIFY command does not always return the
correct number of sectors. The number observed was 1 too high
on a Compaq Deskpro 386/16.
Early controllers allowed sector sizes of 128, 256, 512 or 1024
to be set by bits in the DRIVE SELECT & HEAD register. However,
more recent controllers insist that you set the bits to specify
512 bytes. My driver is written to allow different values, but
it looks like they will never be used. I think I'll leave the
driver as it is; the speed gain from switching to a constant
would be incredibly tiny, and there's always the chance that
this driver will be used to drive Japanese machines that have
1024k sectors (and obviously do NOT have the newer controllers).
A few early controllers had a bug where if one drive had more
than 8 heads and the other drive had less than 8 heads, then the
controller would forget the number of heads on one drive when
the other drive was selected. Since these controllers were
fixed before any 386 machines were made, and even then they had
to have the proper combination of drives on them, this driver
does not include a workaround for the problem. The workaround
is to write a "0" to CONTROL_PORT before every I/O to a drive
with less than 8 heads, and to write "8" before every I/O to a
drive with 8 or more heads.
Some machines need to have bit 3 set in CONTROL_PORT if they're
going to be used with disks with more than 8 heads. Unlike the
bug mentioned above, however, this only needs to be done when the
drive parameters are set, rather than at every I/O. Whether or
not this bit needs to be set has already been determined; this
driver reads a byte from the RAM table that describes the disk
parameters and uses that value ("ControlFlags") when writing to
CONTROL_PORT.
A few early controllers had a bug where multi-sector transfers
across a 256*N cylinder boundary didn't work. Again, however,
we do not expect to find these controllers on systems that run
NT, so we will not include a workaround. The proper
workaround would be to make sure that all transfers end on or
before any 256*N cylinder boundaries.
Controllers are supposed to decrement the sector count register
as they transfer data, so by the end of a successful transfer
the sector count register should always be 0. However, there
are some controllers that sometimes have the original value in
the sector count register when finished. This driver doesn't
query the sector count register in the normal case, so this is
of no consequence. However, when there is an error this driver
MUST query the sector count register, since controller buffering
makes it impossible to know how many sectors were successfully
moved. If the error occurs in that case, it's not a big deal -
the driver will just report that 0 of X sectors were moved,
rather than X-N of X. The file system will use the same
recovery method for either case.
When a disk goes flaky and doesn't interrupt, on some
controllers you can't trust the DRIVE SELECT & HEAD register to
say which drive was last selected. The driver needs to keep
track of this information itself.
Most controllers do things like assert DRQ for a WRITE or get
prepared after a SET DRIVE PARAMETERS pretty quickly - less than
10us. However, a few older controllers seem to be MUCH slower.
This driver will wait a long time in places - longer than the
50us generally given as an upper limit on driver stalling times
- however, it only waits a long time IF necessary, and then it's
generally only at initialization time, or during an error path.
Using SETUP to set the IRQ on a Compaq secondary controller has
no effect on the IRQ register until after a powercycle - the
register isn't updated, so this driver will fail to access the
disks on the modified controller until the machine is
powercycled.
Revision History:
12-18-92 - Tonye - Changed controller initialization so that
the device is first reset (and consequently
interrupts are certain to be disabled), then
initialize the disk devices, only enabling
interrupts on the controller when we are about
to touch the first disk on the controller.
Also changed the isr to return if there isn't
a valid WhichDeviceObject.
12-30-93 - Tonye - Well the above seemed to work pretty well except that
some ide drives seemed to refuse to initially reset.
Fall back to the original initialization sequence,
and just let the isr return if there isn't a
valid WhichDeviceObject.
--*/
//
// Include files.
//
#include "ntddk.h" // various NT definitions
#include "ntdddisk.h" // disk device driver I/O control codes
#include "ntddscsi.h"
#include <atd_plat.h> // this driver's platform dependent stuff
#include <atd_data.h> // this driver's data declarations
#if DBG
extern ULONG AtDebugLevel = 0;
#endif
#ifdef POOL_TAGGING
#ifdef ExAllocatePool
#undef ExAllocatePool
#endif
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,' DtA')
#endif
VOID
AtReWriteDeviceMap(
IN ULONG ControllerNumber,
IN ULONG DiskNumber,
IN ULONG ApparentHeads,
IN ULONG ApparentCyl,
IN ULONG ApparentSec,
IN ULONG ActualHeads,
IN ULONG ActualCyl,
IN ULONG ActualSec
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,DriverEntry)
#pragma alloc_text(INIT,AtInitializeController)
#pragma alloc_text(INIT,AtInitializeDisk)
#pragma alloc_text(INIT,AtGetTranslatedMemory)
#pragma alloc_text(INIT,AtReportUsage)
#pragma alloc_text(INIT,AtDiskControllerInfo)
#pragma alloc_text(INIT,AtDiskIsPcmcia)
#endif
NTSTATUS
AtGetConfigInfo(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath,
IN OUT PCONFIG_DATA ConfigData
);
BOOLEAN
IssueIdentify(
PCONTROLLER_DATA ControllerData,
PUCHAR Buffer,
BOOLEAN Primary
);
NTSTATUS
AtCreateNumericKey(
IN HANDLE Root,
IN ULONG Name,
IN PWSTR Prefix,
OUT PHANDLE NewKey
);
VOID
AtDiskUpdateDeviceObjects(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
VOID
AtFinishPacket(
IN PDISK_EXTENSION DiskExtension,
IN NTSTATUS NtStatus
);
BOOLEAN
AtDiskWriteWithSync(
IN PVOID Context
);
BOOLEAN
AtDiskReadWithSync(
IN PVOID Context
);
typedef struct _BAD_PCI_BLOCK {
PVOID DiskExtension;
PVOID Buffer;
} BAD_PCI_BLOCK,*PBAD_PCI_BLOCK;
VOID
AtMarkSkew(
IN ULONG ControllerNumber,
IN ULONG DiskNumber,
IN ULONG Skew
);
#define MAX_SEC_TO_TRANS (128)
NTSTATUS
DriverEntry(
IN OUT PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
This routine is the driver's entry point, called at initialization
time by the I/O system. This routine can be called any number of
times, as long as the IO system and the configuration manager
conspire to give it a different controller to support at each call.
It could also be called a single time and given all of the
controllers at once.
It initializes the passed-in driver object, calls the configuration
manager to learn about the devices that it is to support, and for
each controller to be supported it calls a routine to initialize the
controller (and all disks attached to it).
Arguments:
DriverObject - a pointer to the object that represents this device
driver.
Return Value:
If we successfully initialize at least one disk, STATUS_SUCCESS is
returned.
If we don't (because the configuration manager returns an error, or
the configuration manager says that there are no controllers or
drives to support, or no controllers or drives can be successfully
initialized), then the last error encountered is propogated.
--*/
{
PCONFIG_DATA configData; // pointer to config mgr's returned data
NTSTATUS ntStatus;
CCHAR i; // controller init loop index
BOOLEAN partlySuccessful; // TRUE when any controller init'd successfully
//
// We use this to query into the registry as to whether we
// should break at driver entry.
//
RTL_QUERY_REGISTRY_TABLE paramTable[3];
ULONG zero = 0;
ULONG debugLevel = 0;
ULONG shouldBreak = 0;
PWCHAR path;
//
// Since the registry path parameter is a "counted" UNICODE string, it
// might not be zero terminated. For a very short time allocate memory
// to hold the registry path zero terminated so that we can use it to
// delve into the registry.
//
// NOTE NOTE!!!! This is not an architected way of breaking into
// a driver. It happens to work for this driver because the author
// likes to do things this way.
//
if (path = ExAllocatePool(
PagedPool,
RegistryPath->Length+sizeof(WCHAR)
)) {
RtlZeroMemory(
&paramTable[0],
sizeof(paramTable)
);
RtlZeroMemory(
path,
RegistryPath->Length+sizeof(WCHAR)
);
RtlMoveMemory(
path,
RegistryPath->Buffer,
RegistryPath->Length
);
paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[0].Name = L"BreakOnEntry";
paramTable[0].EntryContext = &shouldBreak;
paramTable[0].DefaultType = REG_DWORD;
paramTable[0].DefaultData = &zero;
paramTable[0].DefaultLength = sizeof(ULONG);
paramTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[1].Name = L"DebugLevel";
paramTable[1].EntryContext = &debugLevel;
paramTable[1].DefaultType = REG_DWORD;
paramTable[1].DefaultData = &zero;
paramTable[1].DefaultLength = sizeof(ULONG);
if (!NT_SUCCESS(RtlQueryRegistryValues(
RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
path,
&paramTable[0],
NULL,
NULL
))) {
shouldBreak = 0;
debugLevel = 0;
}
}
//
// We don't need that path anymore.
//
if (path) {
ExFreePool(path);
}
#if DBG
AtDebugLevel = debugLevel;
#endif
if (shouldBreak) {
DbgBreakPoint();
}
//
// Allocate and zero the data structure used during initialization.
//
configData = ExAllocatePool( PagedPool, sizeof ( CONFIG_DATA ) );
if ( configData == NULL ) {
AtDump(
ATERRORS,
("ATDISK: Can't allocate memory for config data\n")
);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory( configData, sizeof( CONFIG_DATA ) );
//
// Get information on the hardware that we're supposed to support.
//
ntStatus = AtGetConfigInfo( DriverObject, RegistryPath, configData );
//
// If AtGetConfigInfo() failed, just exit and propogate the error.
// If it said that there are no controllers to support, return
// STATUS_NO_SUCH_DEVICE.
// Otherwise, try to init the controllers. If at least one succeeds,
// return STATUS_SUCCESS, otherwise return the last error.
//
if ( NT_SUCCESS( ntStatus ) ) {
//
// Initialize the driver object with this driver's entry points.
//
DriverObject->DriverStartIo = AtDiskStartIo;
DriverObject->MajorFunction[IRP_MJ_CREATE] = AtDiskDispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = AtDiskDispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_READ] = AtDiskDispatchReadWrite;
DriverObject->MajorFunction[IRP_MJ_WRITE] = AtDiskDispatchReadWrite;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
AtDiskDispatchDeviceControl;
//
// Call AtInitializeController() for each controller (and its
// attached disks) that we're supposed to support.
//
// Return success if we successfully initialize at least one
// device; return error otherwise.
//
ntStatus = STATUS_NO_SUCH_DEVICE;
partlySuccessful = FALSE;
for ( i = 0;
i < MAXIMUM_NUMBER_OF_CONTROLLERS;
i++ ) {
if (configData->Controller[i].OkToUseThisController) {
//
// If the usage reporting doesn't report
// a conflict then try to initialize the
// controller.
//
if (AtReportUsage(
configData,
(UCHAR)i,
DriverObject)) {
ntStatus = AtInitializeController(
configData,
i,
DriverObject );
} else {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
if ( NT_SUCCESS( ntStatus ) ) {
partlySuccessful = TRUE;
}
}
}
if ( partlySuccessful ) {
ntStatus = STATUS_SUCCESS;
}
}
//
// Delete the data structure we used during initialization.
//
ExFreePool( configData );
return ntStatus;
}
NTSTATUS
AtInitializeController(
IN PCONFIG_DATA ConfigData,
IN CCHAR ControllerNumber,
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
This routine is called at initialization time by
AtDiskInitialize() - once for each controller that the
configuration manager tells it we have to support.
When this routine is called, the configuration data has already been
filled in.
It creates a controller object complete with extension, calls
allocates an interrupt object, resets the controller, and calls
AtInitializeDisk() for each disk attached to the controller.
Arguments:
ConfigData - a pointer to the structure that describes the
controller and the disks attached to it, as given to us by the
configuration manager.
ControllerNumber - which controller in ConfigData we are
initializing.
DriverObject - a pointer to the object that represents this device
driver.
Return Value:
STATUS_SUCCESS if this controller and at least one of its disks were
initialized; an error otherwise.
--*/
{
PCONTROLLER_OBJECT controllerObject;
PCONTROLLER_EXTENSION controllerExtension;
NTSTATUS ntStatus;
CCHAR i; // which disk on this controller
BOOLEAN partlySuccessful; // TRUE when any disk init'd
USHORT MaximumBytesPerInterrupt;
//
// Go through all of the disk configuration data for this
// controller. Find out the maximum value over all of
// the disks for bytes per interrupt. This will
// be used as the size of the read/write garbage can
// that is used to empty/fill the controller cache
// when an error occurs.
//
for (
MaximumBytesPerInterrupt=0,i = 0;
i < MAXIMUM_NUMBER_OF_DISKS_PER_CONTROLLER;
i++
) {
if (ConfigData->Controller[ControllerNumber].Disk[i].BytesPerInterrupt
> MaximumBytesPerInterrupt) {
MaximumBytesPerInterrupt = ConfigData->Controller[ControllerNumber].
Disk[i].
BytesPerInterrupt;
}
}
ASSERT(MaximumBytesPerInterrupt);
//
// Assert that the first four fields in the device extension match
// the first four fields in the partition structure.
//
ASSERT(FIELD_OFFSET(PARTITION_EXTENSION,Pi) ==
FIELD_OFFSET(DISK_EXTENSION,Pi));
ASSERT(FIELD_OFFSET(PARTITION_EXTENSION,Partition0) ==
FIELD_OFFSET(DISK_EXTENSION,Partition0));
ASSERT(FIELD_OFFSET(PARTITION_EXTENSION,PartitionOrdinal) ==
FIELD_OFFSET(DISK_EXTENSION,PartitionOrdinal));
ASSERT(FIELD_OFFSET(PARTITION_EXTENSION,NextPartition) ==
FIELD_OFFSET(DISK_EXTENSION,NextPartition));
//
// Create the controller object and extension. Make sure they point
// to each other.
//
controllerObject = IoCreateController( sizeof( CONTROLLER_EXTENSION )+
(MaximumBytesPerInterrupt-1));
if ( controllerObject == NULL ) {
AtDump(
ATERRORS,
("ATDISK: Couldn't create the controller object.\n")
);
return STATUS_INSUFFICIENT_RESOURCES;
}
controllerExtension =
( PCONTROLLER_EXTENSION )( controllerObject->ControllerExtension );
//
// Make sure that the extension is clean.
//
RtlZeroMemory(
controllerExtension,
sizeof(CONTROLLER_EXTENSION)
);
controllerExtension->ControllerNumber = ControllerNumber;
controllerExtension->BadPciAdapter =
ConfigData->Controller[ControllerNumber].BadPciAdapter;
controllerExtension->ControllerObject = controllerObject;
controllerExtension->ControllerAddress =
ConfigData->Controller[ControllerNumber].ControllerBaseAddress;
controllerExtension->ControlPortAddress =
ConfigData->Controller[ControllerNumber].ControlPortAddress;
//
// Reset the controller here. We don't re-initialize interrupts YET.
// We don't want to enable interrupts yet, because we have no device objects
// for them. We'll enable the interrupts the first time we talk
// to first disk during disk initialization a little later. We are
// connecting to the interrupt because the disk device initialization code
// depends on reading the disk. So, we will delay the enable until
// we are totally ready with the disk device object.
//
// NOTE: We still aren't safe! Suppose we happen to be sharing this
// interrupt with another device. As soon as we connect, we could end
// up seeing an interrupt meant for another device, but, we won't
// be at all ready for it. We will have the interrupt service routine
// make sure that the controller is filled in. If it isn't, the
// ISR will assume that this interrupt is for another device.
//
// NOTE: Some specs say that the before accessing the controller
// after a reset we should wait 10us. Make sure we get that done
// here.
//
// NOTE: Some old ix86 machines need bit 3 set in CONTROL_PORT
// to access heads 8-15. The flag was read from CMOS; we'll grab it
// here and use it while writing to CONTROL_PORT.
//
// Sounds like a good plan right? Well it turns out not to work.
// Some IDE drives simply refuse to initialize if we do it this way.
// We will simply initialize the way we did before, and let the
// ISR return immediately if there isn't a valid WhichDevice.
//
controllerExtension->ControlFlags =
ConfigData->Controller[ControllerNumber].ControlFlags;
//
// Allocate and connect interrupt objects for this device to all of the
// processors on which this device can interrupt.
//
if ( !( NT_SUCCESS( ntStatus = IoConnectInterrupt(
&controllerExtension->InterruptObject,
AtDiskInterruptService,
controllerExtension,
NULL,
ConfigData->Controller[ControllerNumber].ControllerVector,
ConfigData->Controller[ControllerNumber].ControllerIrql,
ConfigData->Controller[ControllerNumber].ControllerIrql,
ConfigData->Controller[ControllerNumber].InterruptMode,
ConfigData->Controller[ControllerNumber].SharableVector,
ConfigData->Controller[ControllerNumber].ProcessorNumber,
ConfigData->Controller[ControllerNumber].SaveFloatState
) ) ) ) {
AtDump(
ATERRORS,
("ATDISK: Couldn't connect to the interrupt\n")
);
goto AtInitializeControllerExit;
}
//
// It doesn't matter whether we have any devices yet. The interrupt
// service routine will simply dismiss any iterrupts until it's ready.
//
// Well wait up to 2 seconds for the controller to accept the reset.
//
WRITE_CONTROLLER(
controllerExtension->ControlPortAddress,
RESET_CONTROLLER );
AtWaitControllerBusy(
controllerExtension->ControllerAddress + STATUS_REGISTER,
20,
75000
);
WRITE_CONTROLLER(
controllerExtension->ControlPortAddress,
( ENABLE_INTERRUPTS | controllerExtension->ControlFlags ) );
//
// For every disk on the controller, call AtInitializeDisk(). Note
// that because of ISA restrictions, there are a maximum of two.
//
partlySuccessful = FALSE;
ntStatus = STATUS_NO_SUCH_DEVICE;
for ( i = 0;
i < MAXIMUM_NUMBER_OF_DISKS_PER_CONTROLLER;
i++ ) {
if ( ConfigData->Controller[ControllerNumber].Disk[i].DriveType != 0 ) {
ntStatus = AtInitializeDisk(
ConfigData,
ControllerNumber,
i,
DriverObject,
controllerExtension );
if ( NT_SUCCESS( ntStatus ) ) {
( *( ConfigData->HardDiskCount ) )++;
partlySuccessful = TRUE;
}
}
} // FOR i = 0-> call AtInitializeDisk()
if ( partlySuccessful ) {
PMAPPED_ADDRESS mappedAddress;
ntStatus = STATUS_SUCCESS;
//
// Allocate and insert mapped address entries for each of the addresses
// mapped for this controller.
//
if ( ConfigData->Controller[ControllerNumber].ControllerBaseMapped ) {
mappedAddress =
ExAllocatePool(
NonPagedPool,
sizeof( MAPPED_ADDRESS )
);
if ( mappedAddress == NULL ) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
} else {
mappedAddress->NextMappedAddress = NULL;
mappedAddress->MappedAddress =
ConfigData->Controller[ControllerNumber].ControllerBaseAddress;
mappedAddress->NumberOfBytes =
ConfigData->Controller[ControllerNumber].RangeOfControllerBase;
mappedAddress->IoAddress =
ConfigData->Controller[ControllerNumber].OriginalControllerBaseAddress;
mappedAddress->BusNumber =
ConfigData->Controller[ControllerNumber].BusNumber;
controllerExtension->MappedAddressList = mappedAddress;
}
}
if ( ConfigData->Controller[ControllerNumber].ControlPortMapped ) {
mappedAddress =
ExAllocatePool(
NonPagedPool,
sizeof( MAPPED_ADDRESS )
);
if ( mappedAddress == NULL ) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
} else {
mappedAddress->NextMappedAddress =
controllerExtension->MappedAddressList;
mappedAddress->MappedAddress =
ConfigData->Controller[ControllerNumber].ControlPortAddress;
mappedAddress->NumberOfBytes =
ConfigData->Controller[ControllerNumber].RangeOfControlPort;
mappedAddress->IoAddress =
ConfigData->Controller[ControllerNumber].OriginalControlPortAddress;
mappedAddress->BusNumber =
ConfigData->Controller[ControllerNumber].BusNumber;
controllerExtension->MappedAddressList = mappedAddress;
}
}
}
AtInitializeControllerExit:
if ( !NT_SUCCESS( ntStatus ) ) {
//
// Delete everything allocated by this routine. We know that the
// controller object exists, or we would have already returned.
//
if ( controllerExtension->InterruptObject != NULL ) {
IoDisconnectInterrupt( controllerExtension->InterruptObject );
}
if ( controllerExtension->MappedAddressList != NULL ) {
ExFreePool( controllerExtension->MappedAddressList );
}
IoDeleteController( controllerObject );
}
return ntStatus;
}
NTSTATUS
AtInitializeDisk(
IN PCONFIG_DATA ConfigData,
IN CCHAR ControllerNum,
IN CCHAR DiskNum,
IN PDRIVER_OBJECT DriverObject,
IN OUT PCONTROLLER_EXTENSION ControllerExtension
)
/*++
Routine Description:
This routine is called at initialization time by
AtInitializeController(), once for each disk that we are supporting
on the controller.
When called, the controller has already been reset and interrupts are
enabled.
It creates a directory for the device objects, allocates and
initializes a device object for the disk, sets the drive parameters,
reads the partition table, and allocates partition objects (which
are also device objects, but with a different extension) for each
partition.
Arguments:
ConfigData - a pointer to the structure that describes the
controller and the disks attached to it, as given to us by the
configuration manager.
ControllerNum - which controller in ConfigData we're working on.
DiskNum - which disk on the current controller we're working on.
DriverObject - a pointer to the object that represents this device
driver.
ControllerExtension - a pointer to the space allocated by this driver
that is associated with the controller object.
Return Value:
STATUS_SUCCESS if this disk is initialized; an error otherwise.
--*/
{
UCHAR ntNameBuffer[256];
STRING ntNameString;
UNICODE_STRING ntUnicodeString;
PDRIVE_LAYOUT_INFORMATION partitionList = NULL;
OBJECT_ATTRIBUTES objectAttributes; // for the directory object
HANDLE handle = NULL; // handle to the directory object
PDEVICE_OBJECT deviceObject = NULL; // ptr to part 0 device object
PDEVICE_OBJECT partitionObject; // ptr to a part x device object
PDEVICE_OBJECT nextPartition; // ptr for walking chain
PDEVICE_OBJECT *partitionPointer;
PDISK_EXTENSION diskExtension = NULL; // ptr to part 0 device extension
PPARTITION_EXTENSION partitionExtension; // ptr to part x device extension
NTSTATUS ntStatus;
ULONG partitionNumber = 0; // which partition we're working on
BOOLEAN timerWasStarted = FALSE; // TRUE when IoStartTimer called
BOOLEAN HookerGeometry; // Set if we find a hooker we need to
// do geometry calcs with.
//
// Create a permanent object directory for partitions, then make it
// temporary so that we can close it at any time and it will go away.
//
sprintf(
ntNameBuffer,
"\\Device\\Harddisk%d",
*( ConfigData->HardDiskCount ) );
RtlInitString( &ntNameString, ntNameBuffer );
ntStatus = RtlAnsiStringToUnicodeString(
&ntUnicodeString,
&ntNameString,
TRUE );
if ( !NT_SUCCESS( ntStatus ) ) {
AtDump(
ATERRORS,
("ATDISK: Couldn't create the unicode device name\n")
);
goto AtInitializeDiskExit;
}
InitializeObjectAttributes(
&objectAttributes,
&ntUnicodeString,
OBJ_PERMANENT,
NULL,
NULL );
ntStatus = ZwCreateDirectoryObject(
&handle,
DIRECTORY_ALL_ACCESS,
&objectAttributes );
RtlFreeUnicodeString( &ntUnicodeString );
if ( !NT_SUCCESS( ntStatus ) ) {
AtDump(
ATERRORS,
("ATDISK: Couldn't create the directory object\n")
);
goto AtInitializeDiskExit;
}
ZwMakeTemporaryObject( handle );
//
// create partition 0 object
//
sprintf(
ntNameBuffer,
"\\Device\\Harddisk%d\\Partition0",
*( ConfigData->HardDiskCount ) );
RtlInitString( &ntNameString, ntNameBuffer );
ntStatus = RtlAnsiStringToUnicodeString(
&ntUnicodeString,
&ntNameString,
TRUE );
if ( !NT_SUCCESS( ntStatus ) ) {
AtDump(
ATERRORS,
("ATDISK: Couldn't create the partition unicode name\n")
);
goto AtInitializeDiskExit;
}
ntStatus = IoCreateDevice(
DriverObject,
sizeof( DISK_EXTENSION ),
&ntUnicodeString,
FILE_DEVICE_DISK,
0,
FALSE,
&deviceObject );
if ( !NT_SUCCESS( ntStatus ) ) {
AtDump(
ATERRORS,
("ATDISK: Couldn't create the device object\n")
);
goto AtInitializeDiskExit;
}
RtlFreeUnicodeString( &ntUnicodeString );
//
// Initialize partition 0 device object and extension. Store pointer
// to controller extension in partition 0's extension.
//
deviceObject->Flags |= DO_DIRECT_IO;
deviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT;
diskExtension = ( PDISK_EXTENSION )( deviceObject->DeviceExtension );
diskExtension->DiskNumber = *ConfigData->HardDiskCount;
diskExtension->ControllerExtension = ControllerExtension;
diskExtension->Partition0 = diskExtension;
diskExtension->DeviceObject = deviceObject;
diskExtension->DirectoryHandle = handle;
diskExtension->PacketIsBeingRetried = FALSE;
//
// Set the device unit. We must examine DiskNum for the case
// where the controller says this is drive 2, but we were unable to
// initialize the first drive.
//
if ( DiskNum == 0 ) {
diskExtension->DeviceUnit = DRIVE_1;
} else {
diskExtension->DeviceUnit = DRIVE_2;
}
//
// Set up pointers between disks and from the controller extension.
// We examine Disk1 rather than DiskNum because we never want
// Disk1 to be NULL, regardless of which unit number the controller
// considers it to be.
//
if ( ControllerExtension->Disk1 == NULL ) {
ControllerExtension->Disk1 = diskExtension;
diskExtension->OtherDiskExtension = NULL;
//
// Since this is the first device object we've successfully
// initialized, let's attach the timer stuff (which actually
// belongs with the controller extension, but requires a device
// object) to this device object.
//
ControllerExtension->InterruptTimer = CANCEL_TIMER;
IoInitializeTimer(
deviceObject,
AtDiskCheckTimer,
ControllerExtension );
IoStartTimer( deviceObject );
timerWasStarted = TRUE;
} else {
//
// This is the second disk we have initialized. Link the disk
// extensions together.
//
ControllerExtension->Disk2 = diskExtension;
diskExtension->OtherDiskExtension = ControllerExtension->Disk1;
ControllerExtension->Disk1->OtherDiskExtension = diskExtension;
}
//
// Fill in device-specific numbers that were obtained from the
// configuration manager.
//
diskExtension->PretendNumberOfCylinders =
ConfigData->Controller[ControllerNum].Disk[DiskNum].PretendNumberOfCylinders;
diskExtension->PretendTracksPerCylinder =
ConfigData->Controller[ControllerNum].Disk[DiskNum].PretendTracksPerCylinder;
diskExtension->PretendSectorsPerTrack =
ConfigData->Controller[ControllerNum].Disk[DiskNum].PretendSectorsPerTrack;
diskExtension->NumberOfCylinders =
ConfigData->Controller[ControllerNum].Disk[DiskNum].NumberOfCylinders;
diskExtension->TracksPerCylinder =
ConfigData->Controller[ControllerNum].Disk[DiskNum].TracksPerCylinder;
diskExtension->SectorsPerTrack =
ConfigData->Controller[ControllerNum].Disk[DiskNum].SectorsPerTrack;
diskExtension->BytesPerSector =
ConfigData->Controller[ControllerNum].Disk[DiskNum].BytesPerSector;
diskExtension->BytesPerInterrupt =
ConfigData->Controller[ControllerNum].Disk[DiskNum].BytesPerInterrupt;
diskExtension->WritePrecomp =
ConfigData->Controller[ControllerNum].Disk[DiskNum].WritePrecomp;
diskExtension->ReadCommand =
ConfigData->Controller[ControllerNum].Disk[DiskNum].ReadCommand;
diskExtension->WriteCommand =
ConfigData->Controller[ControllerNum].Disk[DiskNum].WriteCommand;
diskExtension->VerifyCommand =
ConfigData->Controller[ControllerNum].Disk[DiskNum].VerifyCommand;
diskExtension->UseLBAMode =
ConfigData->Controller[ControllerNum].Disk[DiskNum].UseLBAMode;
AtDump(
ATINIT,
("ATDISK: Controller %d Disk %d Geometry:\n"
" Appa Cyl: %x\n"
" Appa Hea: %x\n"
" Appa Sec: %x\n"
" Cyl: %x\n"
" Hea: %x\n"
" Sec: %x\n",
ControllerNum,
DiskNum,
diskExtension->PretendNumberOfCylinders,
diskExtension->PretendTracksPerCylinder,
diskExtension->PretendSectorsPerTrack,
diskExtension->NumberOfCylinders,
diskExtension->TracksPerCylinder,
diskExtension->SectorsPerTrack)
);
//
// Determine the size of partition 0 (the whole disk).
//
diskExtension->Pi.StartingOffset.QuadPart = 0;
diskExtension->Pi.PartitionLength.QuadPart =
(UInt32x32To64(
diskExtension->SectorsPerTrack,
diskExtension->BytesPerSector
) *
diskExtension->NumberOfCylinders) *
diskExtension->TracksPerCylinder;
//
// Given the sector size, figure out how many times we have to shift
// a byte value to determine a sector value. Note that only there
// are only four sector sizes allowed by the controller.
//
switch ( diskExtension->BytesPerSector ) {
case 128: {
diskExtension->ByteShiftToSector = 7;
break;
}
case 256: {
diskExtension->ByteShiftToSector = 8;
break;
}
case 512: {
diskExtension->ByteShiftToSector = 9;
break;
}
case 1024: {
diskExtension->ByteShiftToSector = 10;
break;
}
default: {
AtDump(
ATBUGCHECK,
("AtDisk ERROR: unsupported sector size %x\n",
diskExtension->BytesPerSector));
diskExtension->ByteShiftToSector = 9;
break;
}
}
//
// Initialize DPC in partition 0 object
//
IoInitializeDpcRequest( deviceObject, AtDiskDeferredProcedure );
//
// There is a very small chance that the controller is still busy
// after the reset (the reset is very fast on most controllers, but
// there are a few slow ones). It won't give an interrupt, so if
// it's busy we'll just have to wait for it here. If it's not ready
// after 3 seconds, just blast ahead anyway...we'll time out in
// IoReadPartitionTable() and deal with it then.
//
ntStatus = AtWaitControllerReady( ControllerExtension, 20, 150000 );
if (!NT_SUCCESS(ntStatus)) {
AtDump(
ATERRORS,
("ATDISK: Disk hasn't come back from the reset after 3 seconds\n")
);
}
//
// First we'll set up the disk so that it doesn't revert to power
// on defaults after a controller reset. Then we'll set it up
// so that the write cache is disabled. If this works great. If
// not, well, we're no worse off then we were before.
//
ControllerExtension->WhichDeviceObject = diskExtension->DeviceObject;
//
// Select the right drive.
//
WRITE_CONTROLLER(
ControllerExtension->ControllerAddress + DRIVE_HEAD_REGISTER,
diskExtension->DeviceUnit
);
//
// Disable the reverting to power on.
//
WRITE_CONTROLLER(
ControllerExtension->ControllerAddress + WRITE_PRECOMP_REGISTER,
0x66
);
ControllerExtension->InterruptTimer = START_TIMER;
WRITE_CONTROLLER(
ControllerExtension->ControllerAddress + COMMAND_REGISTER,
0xef );
AtWaitControllerReady( ControllerExtension, 10, 15000 );
WRITE_CONTROLLER(
ControllerExtension->ControllerAddress + DRIVE_HEAD_REGISTER,
diskExtension->DeviceUnit
);
if (ConfigData->Controller[ControllerNum].Disk[DiskNum].DisableReadCache) {
//
// Disable the read cache.
//
WRITE_CONTROLLER(
ControllerExtension->ControllerAddress + WRITE_PRECOMP_REGISTER,
0x55
);
ControllerExtension->InterruptTimer = START_TIMER;
WRITE_CONTROLLER(
ControllerExtension->ControllerAddress + COMMAND_REGISTER,
0xef );
AtWaitControllerReady( ControllerExtension, 10, 15000 );
AtLogError(
deviceObject,
0,
0,
0,
18,
STATUS_SUCCESS,
IO_WRN_BAD_FIRMWARE,
ERROR_LOG_TYPE_TIMEOUT,
0,
0,
0,
0,
0,
0,
0
);
}
if (ConfigData->Controller[ControllerNum].Disk[DiskNum].DisableWriteCache) {
//
// Disable the write cache.
//
WRITE_CONTROLLER(
ControllerExtension->ControllerAddress + WRITE_PRECOMP_REGISTER,
0x82
);
ControllerExtension->InterruptTimer = START_TIMER;
WRITE_CONTROLLER(
ControllerExtension->ControllerAddress + COMMAND_REGISTER,
0xef );
AtWaitControllerReady( ControllerExtension, 10, 15000 );
AtLogError(
deviceObject,
0,
0,
0,
19,
STATUS_SUCCESS,
IO_WRN_BAD_FIRMWARE,
ERROR_LOG_TYPE_TIMEOUT,
0,
0,
0,
0,
0,
0,
0
);
}
//
// Set up drive parameters. This will cause an interrupt.
// Don't need to allocate controller, since this is init time and DPC
// won't be invoked. Even though we're waiting here for the BUSY bit,
// we need to start the timer since that's the ISR's indication that
// the interrupt was expected.
//
ControllerExtension->WhichDeviceObject = diskExtension->DeviceObject;
ControllerExtension->InterruptTimer = START_TIMER;
if ( NT_SUCCESS( ntStatus ) ) {
WRITE_CONTROLLER(
ControllerExtension->ControllerAddress + DRIVE_HEAD_REGISTER,
( diskExtension->DeviceUnit |
( diskExtension->TracksPerCylinder - 1 ) ) );
WRITE_CONTROLLER(
ControllerExtension->ControllerAddress + SECTOR_COUNT_REGISTER,
diskExtension->SectorsPerTrack );
WRITE_CONTROLLER(
ControllerExtension->ControllerAddress + COMMAND_REGISTER,
SET_DRIVE_PARAMETERS_COMMAND );
}
//
// We can't read the partition table until the controller is ready.
// The SET_DRIVE_PARAMETERS command *should* be ~10us; let's wait
// for it here. (It will interrupt, but we want to finish the
// initialization here rather than in a DPC). Note that some machines
// are way out of spec, and take a good portion of a second, so we'll
// wait as long as 3 seconds. If we really get to 3 seconds, just
// blast ahead; we'll time out in IoReadPartitionTable() and deal
// with it there. We ordinarily wouldn't wait this long, but with
// some controllers we HAVE to, and this is just init time.
//
ntStatus = AtWaitControllerReady( ControllerExtension, 20, 150000 );
if (!NT_SUCCESS(ntStatus)) {
AtDump(
ATERRORS,
("ATDISK: Disk hasn't come back from setting the parameters after 3 seconds\n")
);
}
//
// Turn off the timer whether we succeeded or not - we don't have the
// controller object, so we don't want the timer to expire yet. It
// will when we try to read the partition table.
//
ControllerExtension->InterruptTimer = CANCEL_TIMER;
//
// Now recalibrate the drive. Many drives don't need this, but some
// seem to go wacky later if this isn't done.
//
ControllerExtension->WhichDeviceObject = diskExtension->DeviceObject;
ControllerExtension->InterruptTimer = START_TIMER_FOR_RECALIBRATE;
if ( NT_SUCCESS( ntStatus ) ) {
WRITE_CONTROLLER(
ControllerExtension->ControllerAddress + DRIVE_HEAD_REGISTER,
( diskExtension->DeviceUnit |
( diskExtension->TracksPerCylinder - 1 ) ) );
WRITE_CONTROLLER(
ControllerExtension->ControllerAddress + COMMAND_REGISTER,
RECALIBRATE_COMMAND );
}
ntStatus = AtWaitControllerReady( ControllerExtension, 20, 150000 );
if (!NT_SUCCESS(ntStatus)) {
AtDump(
ATERRORS,
("ATDISK: Disk hasn't come back from the recal after 3 seconds\n")
);
}
//
// See if this is a large disk that was partitioned by an MBR hooker.
// If so for the appropriate versions of DM we have to skew
// all IO by 63 sectors. If we find DM then we have to
//
// 1) Recalc the size of the drive that we present to utilities.
//
// 2) For EZDrive we don't care. Partition code take care of things.
// (We still have to do the gometry though.
//
{
PULONG Skew;
HookerGeometry = FALSE;
HalExamineMBR(
diskExtension->DeviceObject,
ConfigData->Controller[ControllerNum].Disk[DiskNum].BytesPerSector,
(ULONG)0x54,
&Skew
);
if (Skew) {
diskExtension->DMSkew = *Skew;
diskExtension->DMControl = TRUE;
AtMarkSkew(
ControllerNum,
DiskNum,
0x54
);
ExFreePool(Skew);
HookerGeometry = TRUE;
} else {
diskExtension->DMSkew = 0;
diskExtension->DMControl = FALSE;
diskExtension->DMByteSkew.QuadPart = 0;
//
// Look for EZDrive
//
HalExamineMBR(
diskExtension->DeviceObject,
ConfigData->Controller[ControllerNum].Disk[DiskNum].BytesPerSector,
(ULONG)0x55,
&Skew
);
if (Skew) {
//
// EZdrive found.
//
ExFreePool(Skew);
AtMarkSkew(
ControllerNum,
DiskNum,
0x55
);
HookerGeometry = TRUE;
}
}
}
if (HookerGeometry) {
//
//
//
ULONG numberOfHeads = ConfigData->Controller[ControllerNum].Disk[DiskNum].IdentifyTracksPerCylinder;
ULONG numberOfCyl = ConfigData->Controller[ControllerNum].Disk[DiskNum].IdentifyNumberOfCylinders;
while (numberOfCyl > 1024) {
numberOfHeads = numberOfHeads*2;
numberOfCyl = numberOfCyl/2;
}
//
// int 13 values are always 1 less.
//
numberOfHeads -= 1;
numberOfCyl -= 1;
//
// DM/EZDrive reserves the CE cylinder
//
numberOfCyl -= 1;
diskExtension->PretendNumberOfCylinders = (USHORT)numberOfCyl + 1;
diskExtension->PretendTracksPerCylinder = (USHORT)numberOfHeads + 1;
diskExtension->NumberOfCylinders = ConfigData->Controller[ControllerNum].Disk[DiskNum].IdentifyNumberOfCylinders;
diskExtension->TracksPerCylinder = ConfigData->Controller[ControllerNum].Disk[DiskNum].TracksPerCylinder;
AtDump(
ATINIT,
("ATDISK: Controller %d Disk %d Geometry: ***DM/EZ*** adjusted\n"
" Appa Cyl: %x\n"
" Appa Hea: %x\n"
" Appa Sec: %x\n"
" Cyl: %x\n"
" Hea: %x\n"
" Sec: %x\n",
ControllerNum,
DiskNum,
diskExtension->PretendNumberOfCylinders,
diskExtension->PretendTracksPerCylinder,
diskExtension->PretendSectorsPerTrack,
diskExtension->NumberOfCylinders,
diskExtension->TracksPerCylinder,
diskExtension->SectorsPerTrack)
);
diskExtension->Pi.PartitionLength.QuadPart =
(UInt32x32To64(
diskExtension->SectorsPerTrack,
diskExtension->BytesPerSector
) *
diskExtension->NumberOfCylinders) *
diskExtension->TracksPerCylinder;
diskExtension->DMByteSkew.QuadPart =
UInt32x32To64(
diskExtension->DMSkew,
diskExtension->BytesPerSector
);
AtReWriteDeviceMap(
ControllerNum,
DiskNum,
diskExtension->PretendTracksPerCylinder,
diskExtension->PretendNumberOfCylinders,
diskExtension->PretendSectorsPerTrack,
diskExtension->TracksPerCylinder,
diskExtension->NumberOfCylinders,
diskExtension->SectorsPerTrack
);
}
//
// Turn off the timer whether we succeeded or not - we don't have the
// controller object, so we don't want the timer to expire yet. It
// will when we try to read the partition table.
//
ControllerExtension->InterruptTimer = CANCEL_TIMER;
//
// Read partition table
//
ntStatus = IoReadPartitionTable(
diskExtension->DeviceObject,
ConfigData->Controller[ControllerNum].Disk[DiskNum].BytesPerSector,
TRUE,
&partitionList );
//
// For each partition other than partition 0, create and initialize a
// partition object. Chain the partition objects together so we can
// delete them if necessary. If IoReadPartitionTable() failed, just
// skip this section, but keep partition 0 around so the disk can be
// formatted or somesuch.
//
if ( !NT_SUCCESS( ntStatus ) ) {
//
// IoReadPartitionTable() failed, but force success so we don't
// unload since we still have partition 0 to support.
//
AtDump(
ATERRORS,
("ATDISK: Couldn't read the partition table\n")
);
ntStatus = STATUS_SUCCESS;
} else {
//
// IoReadPartitionTable() didn't return error, so initialize the
// partitions.
//
partitionPointer = &diskExtension->NextPartition;
for ( partitionNumber = 0;
partitionNumber < partitionList->PartitionCount;
partitionNumber++ ) {
//
// Create the device, with a UNICODE name such as
// \\Device\Harddisk0\Partition1
//
sprintf(
ntNameBuffer,
"\\Device\\Harddisk%d\\Partition%d",
*( ConfigData->HardDiskCount ),
partitionNumber + 1 );
RtlInitString ( &ntNameString, ntNameBuffer );
ntStatus = RtlAnsiStringToUnicodeString(
&ntUnicodeString,
&ntNameString,
TRUE );
if ( !NT_SUCCESS( ntStatus ) ) {
AtDump(
ATERRORS,
("ATDISK: Couldn't create the partition 0"
" partition objects\n")
);
goto AtInitializeDiskExit;
}
ntStatus = IoCreateDevice(
DriverObject,
sizeof( PARTITION_EXTENSION ),
&ntUnicodeString,
FILE_DEVICE_DISK,
0,
FALSE,
&partitionObject );
if ( !NT_SUCCESS( ntStatus ) ) {
AtDump(
ATERRORS,
("ATDISK: Couldn't create the partition 0"
" partition devices\n")
);
RtlFreeUnicodeString( &ntUnicodeString );
goto AtInitializeDiskExit;
}
RtlFreeUnicodeString( &ntUnicodeString );
//
// Now initialize the partition object and extension.
//
partitionObject->Flags |= DO_DIRECT_IO;
partitionExtension = partitionObject->DeviceExtension;
partitionExtension->PartitionOrdinal =
partitionExtension->Pi.PartitionNumber = partitionNumber + 1;
partitionExtension->Pi.PartitionType =
partitionList->PartitionEntry[partitionNumber].PartitionType;
partitionExtension->Pi.BootIndicator =
partitionList->PartitionEntry[partitionNumber].BootIndicator;
partitionExtension->Pi.StartingOffset =
partitionList->PartitionEntry[partitionNumber].StartingOffset;
partitionExtension->Pi.PartitionLength =
partitionList->PartitionEntry[partitionNumber].PartitionLength;
partitionExtension->Pi.HiddenSectors =
partitionList->PartitionEntry[partitionNumber].HiddenSectors;
partitionExtension->Partition0 = diskExtension;
partitionExtension->NextPartition = NULL;
*partitionPointer = partitionObject;
partitionPointer = &partitionExtension->NextPartition;
} // FOR partitionNumber = 1-> call IoCreateDevice, etc
} // if IoReadPartitionTable() didn't return error
AtInitializeDiskExit:
if ( !NT_SUCCESS( ntStatus ) ) {
//
// Delete everything that this routine has allocated.
//
// First the chain of partition objects, then the device
// object, then the object directory.
//
if ( diskExtension != NULL ) {
nextPartition = diskExtension->NextPartition;
while ( nextPartition != NULL ) {
partitionObject = nextPartition;
partitionExtension =
( PPARTITION_EXTENSION )( partitionObject->DeviceExtension );
nextPartition = partitionExtension->NextPartition;
IoDeleteDevice( partitionObject );
}
}
if ( timerWasStarted ) {
IoStopTimer( deviceObject );
}
if ( deviceObject != NULL ) {
IoDeleteDevice( deviceObject );
}
if ( handle != NULL ) {
ZwClose( handle );
}
//
// If this is the first disk, make sure Disk1 is null so that its
// spot can be taken by the second disk when we initialize it.
//
if ( DiskNum == 0 ) {
ControllerExtension->Disk1 = NULL;
}
} // if not success, delete everything
//
// Delete the buffer allocated for the partition list.
//
if ( partitionList != NULL ) {
ExFreePool( partitionList );
}
return ntStatus;
}
NTSTATUS
AtDiskDispatchCreateClose(
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp
)
/*++
Routine Description:
This routine is called only rarely by the I/O system; it's mainly
for layered drivers to call. All it does is complete the IRP
successfully.
Arguments:
DeviceObject - a pointer to the object that represents the device
that I/O is to be done on.
Irp - a pointer to the I/O Request Packet for this request.
Return Value:
Always returns STATUS_SUCCESS, since this is a null operation.
--*/
{
UNREFERENCED_PARAMETER( DeviceObject );
//
// Null operation. Do not give an I/O boost since no I/O was
// actually done. IoStatus.Information should be
// FILE_OPENED for an open; it's undefined for a close.
//
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = FILE_OPENED;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_SUCCESS;
}
NTSTATUS
AtDiskDispatchDeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O system to perform a device I/O
control function.
Arguments:
DeviceObject - a pointer to the object that represents the device
that I/O is to be done on.
Irp - a pointer to the I/O Request Packet for this request.
Return Value:
STATUS_SUCCESS if recognized I/O control code,
STATUS_INVALID_DEVICE_REQUEST otherwise.
--*/
{
PPARTITION_EXTENSION partitionExtension;
PDISK_EXTENSION diskExtension;
PCONTROLLER_EXTENSION controllerExtension;
PIO_STACK_LOCATION irpSp;
NTSTATUS ntStatus;
CCHAR ioIncrement = IO_NO_INCREMENT; // assume no I/O will be done
//
// Set up necessary object and extension pointers.
//
partitionExtension = DeviceObject->DeviceExtension;
diskExtension = partitionExtension->Partition0;
controllerExtension = diskExtension->ControllerExtension;
irpSp = IoGetCurrentIrpStackLocation( Irp );
//
// Assume failure.
//
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
//
// Determine which I/O control code was specified.
//
switch ( irpSp->Parameters.DeviceIoControl.IoControlCode ) {
case SMART_GET_VERSION:
//
// Returns the version information and mask describing this drive
// to a SMART application.
//
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(GETVERSIONINPARAMS)) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
} else {
PGETVERSIONINPARAMS versionParameters = (PGETVERSIONINPARAMS)Irp->AssociatedIrp.SystemBuffer;
ULONG controllerNumber,deviceNumber;
//
// Version and revision per SMART 1.03
//
versionParameters->bVersion = 1;
versionParameters->bRevision = 1;
//
// Indicate that support for IDE IDENTIFY and SMART commands. Atapi
// is not supported by this driver.
//
versionParameters->fCapabilities = (CAP_ATA_ID_CMD | CAP_SMART_CMD);
//
// NOTE: This will not give back atapi devices and will only set the bit
// corresponding to this drive's device object.
// The bit mask is as follows:
//
// Sec Pri
// S M S M
// 3 2 1 0
//
controllerNumber = diskExtension->ControllerExtension->ControllerNumber;
deviceNumber = (diskExtension->DeviceUnit == DRIVE_1) ? 0 : 1;
versionParameters->bIDEDeviceMap = 1 << deviceNumber;
versionParameters->bIDEDeviceMap <<= (controllerNumber * 2);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = sizeof(GETVERSIONINPARAMS);
}
break;
case SMART_RCV_DRIVE_DATA:
//
// Returns Identify data or SMART thresholds / attributes to
// an application.
//
if ( irpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(SENDCMDINPARAMS) - 1) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
} else if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(SENDCMDOUTPARAMS) + 512 -1) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
} else {
SENDCMDINPARAMS cmdInParameters = *((PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer);
if (cmdInParameters.irDriveRegs.bCommandReg == ID_CMD) {
ntStatus = AtDiskDispatchReadWrite( DeviceObject, Irp );
return ntStatus;
} else if (cmdInParameters.irDriveRegs.bCommandReg == SMART_CMD) {
switch (cmdInParameters.irDriveRegs.bFeaturesReg) {
case READ_ATTRIBUTES:
case READ_THRESHOLDS:
ntStatus = AtDiskDispatchReadWrite( DeviceObject, Irp );
return ntStatus;
default:
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
break;
}
} else {
//
// Don't allow anything, except for Identify and SMART_CMD
//
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
}
}
break;
case SMART_SEND_DRIVE_COMMAND:
//
// Allows an application to enable or disable SMART on this drive.
//
if ( irpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(SENDCMDINPARAMS) - 1) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
} else if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(SENDCMDOUTPARAMS) - 1) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
} else {
SENDCMDINPARAMS cmdInParameters = *((PSENDCMDINPARAMS)Irp->AssociatedIrp.SystemBuffer);
//
// Only allow the SMART_CMD command to go through.
//
if (cmdInParameters.irDriveRegs.bCommandReg == SMART_CMD) {
switch (cmdInParameters.irDriveRegs.bFeaturesReg) {
case RETURN_SMART_STATUS:
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
(sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS))) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
break;
}
case ENABLE_DISABLE_AUTOSAVE:
case ENABLE_SMART:
case DISABLE_SMART:
case SAVE_ATTRIBUTE_VALUES:
case EXECUTE_OFFLINE_DIAGS:
ntStatus = AtDiskDispatchReadWrite( DeviceObject, Irp );
return ntStatus;
default:
AtDump(ATERRORS,
("ATDISK: Invalid SMART Sub-command (%x)\n",
cmdInParameters.irDriveRegs.bFeaturesReg));
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
break;
}
} else {
AtDump(ATERRORS,
("ATDISK: Invalid SMART Command (%x)\n",
cmdInParameters.irDriveRegs.bCommandReg));
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
}
}
break;
case IOCTL_DISK_GET_DRIVE_GEOMETRY: {
//
// Return the drive geometry for the specified drive. Note that
// we will return the geometry for the physical drive, regardless
// of which partition was specified for the request.
//
if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof( DISK_GEOMETRY ) ) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
} else {
PDISK_GEOMETRY outputBuffer;
outputBuffer = ( PDISK_GEOMETRY )
Irp->AssociatedIrp.SystemBuffer;
outputBuffer->MediaType = FixedMedia;
outputBuffer->Cylinders.QuadPart =
diskExtension->PretendNumberOfCylinders;
outputBuffer->TracksPerCylinder =
diskExtension->PretendTracksPerCylinder;
outputBuffer->SectorsPerTrack =
diskExtension->PretendSectorsPerTrack;
outputBuffer->BytesPerSector = diskExtension->BytesPerSector;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = sizeof( DISK_GEOMETRY );
}
break;
}
case IOCTL_DISK_GET_PARTITION_INFO: {
//
// Return the information about the partition specified by the
// device object. Note that no information is ever returned
// about the size or partition type of the physical disk, as
// this doesn't make any sense.
//
if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof( PARTITION_INFORMATION ) ) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
} else if ( partitionExtension ==
( PPARTITION_EXTENSION )diskExtension ) {
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
} else {
PPARTITION_INFORMATION outputBuffer;
outputBuffer =
( PPARTITION_INFORMATION )Irp->AssociatedIrp.SystemBuffer;
outputBuffer->PartitionType = partitionExtension->Pi.PartitionType;
outputBuffer->BootIndicator = partitionExtension->Pi.BootIndicator;
outputBuffer->RecognizedPartition = TRUE;
outputBuffer->RewritePartition = FALSE;
outputBuffer->PartitionNumber =
partitionExtension->Pi.PartitionNumber;
outputBuffer->StartingOffset =
partitionExtension->Pi.StartingOffset;
outputBuffer->PartitionLength =
partitionExtension->Pi.PartitionLength;
outputBuffer->HiddenSectors =
partitionExtension->Pi.HiddenSectors;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = sizeof( PARTITION_INFORMATION );
}
break;
}
case IOCTL_DISK_SET_PARTITION_INFO: {
//
// Validate input, then set the partition type.
//
if ( irpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof( SET_PARTITION_INFORMATION ) ) {
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
} else if ( partitionExtension ==
( PPARTITION_EXTENSION )diskExtension ) {
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
} else {
PSET_PARTITION_INFORMATION inputBuffer;
inputBuffer = ( PSET_PARTITION_INFORMATION )
Irp->AssociatedIrp.SystemBuffer;
if ( IoSetPartitionInformation(
diskExtension->DeviceObject,
diskExtension->BytesPerSector,
partitionExtension->PartitionOrdinal,
inputBuffer->PartitionType ) ) {
//
// Set the partition type in the partition extension.
//
partitionExtension->Pi.PartitionType =
inputBuffer->PartitionType;
}
Irp->IoStatus.Status = STATUS_SUCCESS;
}
ioIncrement = IO_DISK_INCREMENT; // I/O was done, so boost thread
break;
}
case IOCTL_DISK_GET_DRIVE_LAYOUT: {
//
// Return the partition layout for the physical drive. Note that
// the layout is returned for the actual physical drive, regardless
// of which partition was specified for the request.
//
if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof( DRIVE_LAYOUT_INFORMATION ) ) {
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
} else {
PDRIVE_LAYOUT_INFORMATION partitionList;
ntStatus = IoReadPartitionTable(
diskExtension->DeviceObject,
diskExtension->BytesPerSector,
FALSE,
&partitionList );
if ( !NT_SUCCESS( ntStatus ) ) {
Irp->IoStatus.Status = ntStatus;
} else {
ULONG tempSize;
//
// The disk layout has been returned in the partitionList
// buffer. Determine its size and, if the data will fit
// into the intermediary buffer, return it.
//
tempSize = FIELD_OFFSET(
DRIVE_LAYOUT_INFORMATION,
PartitionEntry[0] );
tempSize += partitionList->PartitionCount *
sizeof( PARTITION_INFORMATION );
if (tempSize >
irpSp->Parameters.DeviceIoControl.OutputBufferLength) {
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
} else {
RtlMoveMemory( Irp->AssociatedIrp.SystemBuffer,
partitionList,
tempSize );
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = tempSize;
AtDiskUpdateDeviceObjects(
diskExtension->DeviceObject,
Irp
);
}
//
// Free the buffer allocated by reading the partition
// table and update the partition numbers for the caller.
//
ExFreePool( partitionList );
}
}
ioIncrement = IO_DISK_INCREMENT; // I/O was done, so boost thread
break;
}
case IOCTL_DISK_SET_DRIVE_LAYOUT: {
//
// Update the disk with new partition information.
//
PDRIVE_LAYOUT_INFORMATION partitionList =
Irp->AssociatedIrp.SystemBuffer;
//
// Validate buffer length.
//
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(DRIVE_LAYOUT_INFORMATION)) {
Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
break;
}
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
(sizeof(DRIVE_LAYOUT_INFORMATION) +
(partitionList->PartitionCount - 1) *
sizeof(PARTITION_INFORMATION))) {
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
break;
}
//
// Walk through partition table comparing partitions to
// existing partitions to create, delete and change
// device objects as necessary.
//
AtDiskUpdateDeviceObjects(
diskExtension->DeviceObject,
Irp
);
Irp->IoStatus.Status = IoWritePartitionTable(
diskExtension->DeviceObject,
diskExtension->BytesPerSector,
diskExtension->PretendSectorsPerTrack,
diskExtension->PretendTracksPerCylinder,
partitionList );
Irp->IoStatus.Information = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
ioIncrement = IO_DISK_INCREMENT; // I/O was done, so boost thread
break;
}
case IOCTL_DISK_VERIFY: {
//
// Move parameters from the VerifyInformation structure to
// the READ parameters area, so that we'll find them when
// we try to treat this like a READ.
//
PVERIFY_INFORMATION verifyInformation;
verifyInformation = Irp->AssociatedIrp.SystemBuffer;
irpSp->Parameters.Read.ByteOffset.LowPart =
verifyInformation->StartingOffset.LowPart;
irpSp->Parameters.Read.ByteOffset.HighPart =
verifyInformation->StartingOffset.HighPart;
irpSp->Parameters.Read.Length = verifyInformation->Length;
//
// A VERIFY is identical to a READ, except for the fact that no
// data gets transferred. So follow the READ code path.
//
ntStatus = AtDiskDispatchReadWrite( DeviceObject, Irp );
return ntStatus;
}
case IOCTL_DISK_INTERNAL_SET_VERIFY:
//
// If the caller is kernel mode, set the verify bit.
//
if (Irp->RequestorMode == KernelMode) {
DeviceObject->Flags |= DO_VERIFY_VOLUME;
}
Irp->IoStatus.Status = STATUS_SUCCESS;
break;
case IOCTL_DISK_INTERNAL_CLEAR_VERIFY:
//
// If the caller is kernel mode, clear the verify bit.
//
if (Irp->RequestorMode == KernelMode) {
DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
}
Irp->IoStatus.Status = STATUS_SUCCESS;
break;
case IOCTL_SCSI_GET_DUMP_POINTERS:
//
// Get parameters for crash dump driver.
//
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength
< sizeof(DUMP_POINTERS)) {
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
} else {
PDUMP_POINTERS dumpPointers =
(PDUMP_POINTERS)Irp->AssociatedIrp.SystemBuffer;
dumpPointers->AdapterObject = NULL;
dumpPointers->MappedRegisterBase =
&diskExtension->ControllerExtension->MappedAddressList;
dumpPointers->PortConfiguration = NULL;
dumpPointers->CommonBufferVa = NULL;
dumpPointers->CommonBufferPa.QuadPart = 0;
dumpPointers->CommonBufferSize = 0;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = sizeof(DUMP_POINTERS);
}
break;
case IOCTL_DISK_CONTROLLER_NUMBER:
if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof( DISK_CONTROLLER_NUMBER ) ) {
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
} else {
PDISK_CONTROLLER_NUMBER controllerNumber =
(PDISK_CONTROLLER_NUMBER) Irp->AssociatedIrp.SystemBuffer;
controllerNumber->ControllerNumber =
diskExtension->ControllerExtension->ControllerNumber;
controllerNumber->DiskNumber =
diskExtension->DeviceUnit == DRIVE_1 ? 0 : 1;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = sizeof(DISK_CONTROLLER_NUMBER);
}
break;
default: {
//
// The specified I/O control code is unrecognized by this driver.
// The I/O status field in the IRP has already been set to just
// terminate the switch.
//
AtDump(
ATDIAG2,
("Atdisk ERROR: unrecognized IOCTL %x\n",
irpSp->Parameters.DeviceIoControl.IoControlCode));
break;
}
}
//
// Finish the I/O operation by simply completing the packet and returning
// the same status as in the packet itself.
//
ntStatus = Irp->IoStatus.Status;
IoCompleteRequest( Irp, ioIncrement );
return ntStatus;
}
NTSTATUS
AtDiskDispatchReadWrite(
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp
)
/*++
Routine Description:
This routine is called by the I/O system to read or write to a
device that we control. It can also be called by
AtDiskDispatchDeviceControl() to do a VERIFY.
This routine changes from the partition object to the device object,
checks parameters for validity, marks the IRP pending, queues the
IRP for processing by AtDiskStartIo(), and returns.
This routine is not protected by device queues or controller object
ownership, so it cannot change any device or controller extension
variables, and it cannot touch the hardware.
Arguments:
DeviceObject - a pointer to the object that represents the device
that I/O is to be done on.
Irp - a pointer to the I/O Request Packet for this request.
Return Value:
STATUS_INVALID_PARAMETER if parameters are invalid,
STATUS_PENDING otherwise.
--*/
{
PPARTITION_EXTENSION partitionExtension;
PDISK_EXTENSION diskExtension;
PIO_STACK_LOCATION irpSp;
ULONG firstSectorOfRequest;
BOOLEAN isSmartIoctl = FALSE;
//
// Set up necessary object and extension pointers.
//
partitionExtension = DeviceObject->DeviceExtension;
diskExtension = partitionExtension->Partition0;
irpSp = IoGetCurrentIrpStackLocation( Irp );
if (irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
ULONG controlCode = irpSp->Parameters.DeviceIoControl.IoControlCode;
if ((controlCode == SMART_GET_VERSION) ||
(controlCode == SMART_SEND_DRIVE_COMMAND) ||
(controlCode == SMART_RCV_DRIVE_DATA)) {
isSmartIoctl = TRUE;
}
}
if (!isSmartIoctl) {
//
// Check for invalid parameters. It is an error for the starting offset
// + length to go past the end of the partition, or for the length to
// not be a proper multiple of the sector size.
//
// Others are possible, but we don't check them since we trust the
// file system and they aren't deadly.
//
if (((irpSp->Parameters.Read.ByteOffset.QuadPart +
irpSp->Parameters.Read.Length) >
partitionExtension->Pi.PartitionLength.QuadPart ) ||
( irpSp->Parameters.Read.Length &
( diskExtension->BytesPerSector - 1 ) ) ) {
//
// Do not give an I/O boost for parameter errors.
//
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return STATUS_INVALID_PARAMETER;
}
//
// The offset passed in is relative to the start of the partition.
// We always work from partition 0 (the whole disk) so adjust the
// offset.
//
// Take care - if this is a dm controlled partition then we need
// to skew by the skew amount.
//
if (diskExtension->DMControl) {
irpSp->Parameters.Read.ByteOffset.QuadPart +=
diskExtension->DMByteSkew.QuadPart;
}
irpSp->Parameters.Read.ByteOffset.QuadPart +=
partitionExtension->Pi.StartingOffset.QuadPart;
firstSectorOfRequest = (ULONG)(irpSp->Parameters.Read.ByteOffset.QuadPart >>
diskExtension->ByteShiftToSector);
} else {
firstSectorOfRequest = diskExtension->SequenceNumber;
}
//
// Mark Irp pending and queue packet, passing sector number for C-SCAN
//
IoMarkIrpPending( Irp );
IoStartPacket(
diskExtension->DeviceObject,
Irp,
&firstSectorOfRequest,
NULL );
return STATUS_PENDING;
}
VOID
AtDiskStartIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is called at DISPATCH_LEVEL by the I/O system when the
disk being serviced is not busy (i.e. the previous packet has been
finished, which is indicated by calling IoStartNextPacket).
It is also called by the DPC to restart requests when the controller
has been reset due to a hardware glitch.
When called, this device is not busy - so this is the only I/O request
being processed for this disk.
This routine sets up variables in the device extension that pertain
to the operation, and then calls AtInitiate() via
IoAllocateController() to avoid contention between disks for the
controller.
This routine is protected by device queues, but not by controller
ownership. So it can alter device extension variables (there will
be no interrupts until after AtInitiate() is called), but not those in
the controller extension and it can't touch the hardware.
Arguments:
DeviceObject - a pointer to the object that represents the device that
I/O is to be done on.
Irp - a pointer to the I/O Request Packet for this request.
Return Value:
None.
--*/
{
PDISK_EXTENSION diskExtension;
PIO_STACK_LOCATION irpSp;
BOOLEAN isSmartIoctl = FALSE;
//
// Set up necessary object and extension pointers.
//
diskExtension = DeviceObject->DeviceExtension;
diskExtension->SequenceNumber++;
irpSp = IoGetCurrentIrpStackLocation( Irp );
if (irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
ULONG controlCode = irpSp->Parameters.DeviceIoControl.IoControlCode;
if ((controlCode == SMART_GET_VERSION) ||
(controlCode == SMART_SEND_DRIVE_COMMAND) ||
(controlCode == SMART_RCV_DRIVE_DATA)) {
isSmartIoctl = TRUE;
}
}
//
// Copy the operation type (READ or WRITE, or IOCTL which means VERIFY)
// and the total byte length of the operation from the IRP stack
// location to the device extension.
//
diskExtension->OperationType = irpSp->MajorFunction;
if (!isSmartIoctl) {
diskExtension->RemainingRequestLength = irpSp->Parameters.Read.Length;
//
// Calculate starting sector and length of the transfer.
//
diskExtension->FirstSectorOfTransfer = (ULONG)
(irpSp->Parameters.Read.ByteOffset.QuadPart >>
diskExtension->ByteShiftToSector);
//
// The first sector of each transfer of a request is greater than the
// previous one, so the following test will only happen when we're
// working on the first transfer of a request (since the completion
// of the previous request set FirstSectorOfRequest to MAXULONG). That's
// when we want to save the first sector of the request so that
// IoStartNextPacketByKey will get the next packet for C-SCAN.
//
if ( diskExtension->FirstSectorOfTransfer <
diskExtension->FirstSectorOfRequest ) {
diskExtension->FirstSectorOfRequest =
diskExtension->FirstSectorOfTransfer;
}
//
// If possible, this transfer should be the same length as the
// request. However, it is limited to MAX_SEC_TO_TRANS sectors.
//
if ( diskExtension->RemainingRequestLength >
( ULONG )( diskExtension->BytesPerSector * MAX_SEC_TO_TRANS ) ) {
diskExtension->TotalTransferLength =
diskExtension->BytesPerSector * MAX_SEC_TO_TRANS;
} else {
diskExtension->TotalTransferLength =
diskExtension->RemainingRequestLength;
}
diskExtension->RemainingTransferLength = diskExtension->TotalTransferLength;
} else {
//
// Determine if this is a data xfer SMART command.
// The possibly ones are inquiry, read attributes, and read thresholds.
//
if (irpSp->Parameters.DeviceIoControl.IoControlCode == SMART_RCV_DRIVE_DATA) {
diskExtension->RemainingRequestLength = diskExtension->BytesPerSector;
} else {
diskExtension->RemainingRequestLength = 0;
}
diskExtension->TotalTransferLength = diskExtension->RemainingRequestLength;
diskExtension->RemainingTransferLength = diskExtension->TotalTransferLength;
}
//
// Generally, we're not resetting the controller so the retry count
// is initialized to 0. But if the controller is being reset because
// of a failure on this disk, increment the retry count. Also don't
// increment the sequence number if we are doing a retry.
//
if ( diskExtension->PacketIsBeingRetried ) {
AtDump(
ATDIAG2,
("ATDISK: *******************Retrying IRP: %x\n",
Irp)
);
diskExtension->PacketIsBeingRetried = FALSE;
diskExtension->IrpRetryCount++;
} else {
diskExtension->SequenceNumber++;
diskExtension->IrpRetryCount = 0;
}
//
// Get a system-space pointer to the user's buffer. A system
// address must be used because we may already have left the
// original caller's address space.
//
if ( Irp->MdlAddress != NULL ) {
diskExtension->CurrentAddress = MmGetSystemAddressForMdl(
Irp->MdlAddress );
} else if (isSmartIoctl) {
if (irpSp->Parameters.DeviceIoControl.IoControlCode == SMART_RCV_DRIVE_DATA) {
diskExtension->CurrentAddress = Irp->AssociatedIrp.SystemBuffer;
}
}
AtDump(
ATDIAG2,
(
"ATDISK: Irp of Request: %x\n"
" Starting vmem Address of Transfer: %x\n"
" Ending vmem Address of Transfer: %x\n"
" Length of Transfer: %x\n",
Irp,
diskExtension->CurrentAddress,
((PUCHAR)diskExtension->CurrentAddress)+diskExtension->RemainingRequestLength,
diskExtension->RemainingRequestLength
) );
AtDump(
ATDIAG2,
("ATDISK - Sector number is: %x\n"
" CHS: %x-%x-%x\n",
diskExtension->FirstSectorOfTransfer,
diskExtension->FirstSectorOfTransfer /
(diskExtension->SectorsPerTrack *
diskExtension->TracksPerCylinder),
(diskExtension->FirstSectorOfTransfer /
diskExtension->SectorsPerTrack) %
diskExtension->TracksPerCylinder,
diskExtension->FirstSectorOfTransfer %
diskExtension->TracksPerCylinder
) );
//
// Allocate the controller, and then program it to do the operation.
//
IoAllocateController(
diskExtension->ControllerExtension->ControllerObject,
DeviceObject,
AtInitiate,
NULL );
return;
}
IO_ALLOCATION_ACTION
AtInitiate(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID MapRegisterBase,
IN PVOID Context
)
/*++
Routine Description:
This routine is called at DISPATCH_LEVEL by the I/O system when the
controller object is free - either directly from AtDiskStartIo(),
or later if the controller object was held at that time.
The free controller object was allocated on behalf of this IRP just
before calling this routine. So this is the only I/O packet being
processed on this controller. All of the variables in the device
extension for the whole request and for the current transfer are
filled in.
This routine calls the AtStartDevice() to program the controller to
do the operation. It does so via KeSynchronizeExecution() to avoid
contention with interrupts.
This routine is protected both by device queue serialization and by
holding the controller object, so it can change variables in both the
device extension and the controller extension. It does not have to
worry about interrupts until after AtStartDevice() is called.
Arguments:
DeviceObject - a pointer to the object that represents the device
that I/O is to be done on.
Irp - a pointer to the I/O Request Packet for this request.
MapRegisterBase - a reserved pointer.
Context - a pointer to a record that could have been passed by the
IoAllocateController call.
Return Value:
KeepObject is always returned because the controller object needs to
be held while the controller is busy with the command. It will be
released at the end of AtDiskDeferredProcedure().
--*/
{
PDISK_EXTENSION diskExtension;
UNREFERENCED_PARAMETER( Irp );
UNREFERENCED_PARAMETER( MapRegisterBase );
UNREFERENCED_PARAMETER( Context );
diskExtension = DeviceObject->DeviceExtension;
diskExtension->ControllerExtension->BusyCountDown = START_BUSY_COUNTDOWN;
//
// Since the controller object is owned, we can program the
// controller. But that must be done via KeSynchronizeExecution, to
// make sure no interrupts come in while we're touching the controller.
//
// AtStartDevice() might return TRUE or FALSE, but in either case we
// just want to exit so we don't have to check the return code.
//
KeSynchronizeExecution(
diskExtension->ControllerExtension->InterruptObject,
AtStartDevice,
diskExtension );
return KeepObject;
}
BOOLEAN
AtStartDevice(
IN OUT PVOID Context
)
/*++
Routine Description:
This routine is called at DIRQL by AtInitiate() and
AtDiskDeferredProcedure() to actually program the operation on the
controller.
When this routine is called, we don't have to worry about another
processor executing this code or the ISR since it is always called
via KeSynchronizeExecution. We are at DIRQL and we have the
spinlock. Further, the controller object is owned so this is the
only packet being processed on this controller.
This routine is protected by both device queue serialization and by
holding the controller object (as well as by DIRQL on the current
processor and the interrupt spinlock for other processors), so it
can change variables in both the device extension and the controller
extension and touch the hardware as much as it wants.
Arguments:
Context - a pointer to the device extension, passed in by
AtInitiate() when it called KeSynchronizeExecution.
Return Value:
Returns TRUE unless the controller hardware fails to respond.
--*/
{
PDISK_EXTENSION diskExtension;
PCONTROLLER_EXTENSION controllerExtension;
NTSTATUS ntStatus;
ULONG loopCount = 0;
UCHAR controllerStatus;
PIRP irp;
PIO_STACK_LOCATION irpSp;
BOOLEAN isSmartIoctl = FALSE;
//
// Set up necessary object and extension pointers.
//
diskExtension = Context;
controllerExtension = diskExtension->ControllerExtension;
controllerStatus = READ_CONTROLLER(
diskExtension->ControllerExtension->ControllerAddress + STATUS_REGISTER
);
//
// Make sure that the device isn't busy. It could be busy here
// because power saving features on laptops might have spun
// down the disk.
//
if (controllerStatus & BUSY_STATUS) {
controllerExtension->BusyDevice = diskExtension;
//
// We don't want to let this go on forever though. If the
// device hasn't come back within a minute then reset the controller.
//
controllerExtension->BusyCountDown--;
if (controllerExtension->BusyCountDown <= 0) {
controllerExtension->BusyCountDown = START_BUSY_COUNTDOWN;
AtDiskStartReset(diskExtension);
}
return TRUE;
} else {
controllerExtension->BusyCountDown = START_BUSY_COUNTDOWN;
controllerExtension->BusyDevice = NULL;
}
//
// We will soon cause an interrupt that will require servicing by
// the DPC, so set these variables accordingly.
//
controllerExtension->InterruptRequiresDpc = TRUE;
controllerExtension->WhichDeviceObject = diskExtension->DeviceObject;
//
// Also leave around the device object so that incase we get a bad
// error during error recovery we can still find the disk that went
// bad.
//
controllerExtension->FirstFailingDeviceObject = diskExtension->DeviceObject;
controllerExtension->InterruptTimer = START_TIMER;
AtDump(
ATDIAG2,
("ATDISK - programming controller with:\n"
" SC: %x\n"
" SN: %x\n"
" CL: %x\n"
" CH: %x\n"
" DH: %x\n",
( diskExtension->TotalTransferLength /
diskExtension->BytesPerSector ),
( ( diskExtension->FirstSectorOfTransfer %
diskExtension->SectorsPerTrack ) + 1 ),
( diskExtension->FirstSectorOfTransfer /
( diskExtension->SectorsPerTrack *
diskExtension->TracksPerCylinder ) ) & 0xff,
( diskExtension->FirstSectorOfTransfer /
( diskExtension->SectorsPerTrack *
diskExtension->TracksPerCylinder ) >> 8 ),
( CCHAR ) ( ( ( diskExtension->FirstSectorOfTransfer /
diskExtension->SectorsPerTrack ) %
diskExtension->TracksPerCylinder ) |
diskExtension->DeviceUnit )
) );
//
// Program the operation on the controller.
//
irp = diskExtension->DeviceObject->CurrentIrp;
irpSp = IoGetCurrentIrpStackLocation( irp );
if (diskExtension->OperationType == IRP_MJ_DEVICE_CONTROL) {
ULONG controlCode = irpSp->Parameters.DeviceIoControl.IoControlCode;
if ((controlCode == SMART_GET_VERSION) ||
(controlCode == SMART_SEND_DRIVE_COMMAND) ||
(controlCode == SMART_RCV_DRIVE_DATA)) {
isSmartIoctl = TRUE;
}
}
if (!isSmartIoctl) {
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + SECTOR_COUNT_REGISTER,
( diskExtension->TotalTransferLength /
diskExtension->BytesPerSector ) );
if (diskExtension->UseLBAMode &&
((diskExtension->FirstSectorOfTransfer & 0xf0000000) == 0) &&
!diskExtension->IrpRetryCount) {
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + SECTOR_NUMBER_REGISTER,
(UCHAR)(diskExtension->FirstSectorOfTransfer & 0xff)
);
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + CYLINDER_LOW_REGISTER,
(UCHAR)((diskExtension->FirstSectorOfTransfer >> 8) & 0xff)
);
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + CYLINDER_HIGH_REGISTER,
(UCHAR)((diskExtension->FirstSectorOfTransfer >> 16) & 0xff)
);
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + DRIVE_HEAD_REGISTER,
(UCHAR)(((diskExtension->FirstSectorOfTransfer >> 24) & 0x0f) |
(diskExtension->DeviceUnit | 0x40))
);
} else {
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + SECTOR_NUMBER_REGISTER,
( ( diskExtension->FirstSectorOfTransfer %
diskExtension->SectorsPerTrack ) + 1 ) );
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + CYLINDER_LOW_REGISTER,
( diskExtension->FirstSectorOfTransfer /
( diskExtension->SectorsPerTrack *
diskExtension->TracksPerCylinder ) ) );
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + CYLINDER_HIGH_REGISTER,
( diskExtension->FirstSectorOfTransfer /
( diskExtension->SectorsPerTrack *
diskExtension->TracksPerCylinder ) >> 8 ) );
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + DRIVE_HEAD_REGISTER,
( CCHAR ) ( ( ( diskExtension->FirstSectorOfTransfer /
diskExtension->SectorsPerTrack ) %
diskExtension->TracksPerCylinder ) |
diskExtension->DeviceUnit ) );
}
} else {
switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
case SMART_RCV_DRIVE_DATA: {
//
// Returns Identify data or SMART thresholds / attributes to
// an application.
//
PSENDCMDOUTPARAMS cmdOutParameters = (PSENDCMDOUTPARAMS)irp->AssociatedIrp.SystemBuffer;
SENDCMDINPARAMS cmdInParameters = *((PSENDCMDINPARAMS)irp->AssociatedIrp.SystemBuffer);
PIDEREGS regs = &cmdInParameters.irDriveRegs;
ULONG i;
UCHAR statusByte;
RtlZeroMemory(cmdOutParameters, sizeof(SENDCMDOUTPARAMS) -1);
if (cmdInParameters.irDriveRegs.bCommandReg == ID_CMD) {
//
// Select disk 0 or 1.
//
WRITE_CONTROLLER(controllerExtension->ControllerAddress + DRIVE_HEAD_REGISTER, diskExtension->DeviceUnit);
//
// Send IDENTIFY command.
//
WRITE_CONTROLLER(controllerExtension->ControllerAddress + COMMAND_REGISTER,
IDENTIFY_COMMAND);
} else if (cmdInParameters.irDriveRegs.bCommandReg == SMART_CMD) {
switch (cmdInParameters.irDriveRegs.bFeaturesReg) {
case READ_ATTRIBUTES:
case READ_THRESHOLDS:
//
// Select disk 0 or 1.
//
WRITE_CONTROLLER(controllerExtension->ControllerAddress + DRIVE_HEAD_REGISTER, diskExtension->DeviceUnit);
//
// Jam the task file regs with that values provided.
//
WRITE_CONTROLLER(controllerExtension->ControllerAddress + DRIVE_HEAD_REGISTER,
diskExtension->DeviceUnit);
WRITE_CONTROLLER(controllerExtension->ControllerAddress + WRITE_PRECOMP_REGISTER,
regs->bFeaturesReg);
WRITE_CONTROLLER(controllerExtension->ControllerAddress + SECTOR_COUNT_REGISTER,
regs->bSectorCountReg);
WRITE_CONTROLLER(controllerExtension->ControllerAddress + SECTOR_NUMBER_REGISTER,
regs->bSectorNumberReg);
WRITE_CONTROLLER(controllerExtension->ControllerAddress + CYLINDER_LOW_REGISTER,
regs->bCylLowReg);
WRITE_CONTROLLER(controllerExtension->ControllerAddress + CYLINDER_HIGH_REGISTER,
regs->bCylHighReg);
WRITE_CONTROLLER(controllerExtension->ControllerAddress + COMMAND_REGISTER,
regs->bCommandReg);
break;
default:
AtDump(
ATBUGCHECK,
("AtDisk ERROR: invalid operation type %x\n",
diskExtension->OperationType ));
break;
}
} else {
//
// Don't allow anything, except for Identify and SMART_CMD
//
AtDump(
ATBUGCHECK,
("AtDisk ERROR: invalid operation type %x\n",
diskExtension->OperationType ));
break;
}
break;
}
case SMART_SEND_DRIVE_COMMAND: {
PSENDCMDOUTPARAMS cmdOutParameters = (PSENDCMDOUTPARAMS)irp->AssociatedIrp.SystemBuffer;
SENDCMDINPARAMS cmdInParameters = *((PSENDCMDINPARAMS)irp->AssociatedIrp.SystemBuffer);
UCHAR statusByte;
//
// Only allow the SMART_CMD command to go through.
//
if (cmdInParameters.irDriveRegs.bCommandReg == SMART_CMD) {
//
// Get the parameters from the system buffer to a local, as
// the buffer is also the output.
//
PIDEREGS regs = &cmdInParameters.irDriveRegs;
switch (cmdInParameters.irDriveRegs.bFeaturesReg) {
case SAVE_ATTRIBUTE_VALUES:
case RETURN_SMART_STATUS:
case ENABLE_DISABLE_AUTOSAVE:
case ENABLE_SMART:
case DISABLE_SMART:
case EXECUTE_OFFLINE_DIAGS:
//
// Select disk 0 or 1.
//
WRITE_CONTROLLER(controllerExtension->ControllerAddress + DRIVE_HEAD_REGISTER, diskExtension->DeviceUnit);
//
// Jam the task file regs with that values provided.
//
WRITE_CONTROLLER(controllerExtension->ControllerAddress + WRITE_PRECOMP_REGISTER,
regs->bFeaturesReg);
WRITE_CONTROLLER(controllerExtension->ControllerAddress + SECTOR_COUNT_REGISTER,
regs->bSectorCountReg);
WRITE_CONTROLLER(controllerExtension->ControllerAddress + SECTOR_NUMBER_REGISTER,
regs->bSectorNumberReg);
WRITE_CONTROLLER(controllerExtension->ControllerAddress + CYLINDER_LOW_REGISTER,
regs->bCylLowReg);
WRITE_CONTROLLER(controllerExtension->ControllerAddress + CYLINDER_HIGH_REGISTER,
regs->bCylHighReg);
WRITE_CONTROLLER(controllerExtension->ControllerAddress + COMMAND_REGISTER,
regs->bCommandReg);
break;
default:
AtDump(
ATBUGCHECK,
("AtDisk ERROR: invalid operation type %x\n",
diskExtension->OperationType ));
break;
}
} else {
AtDump(
ATBUGCHECK,
("AtDisk ERROR: invalid operation type %x\n",
diskExtension->OperationType ));
}
}
break;
}
}
switch ( diskExtension->OperationType ) {
case IRP_MJ_READ: {
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + COMMAND_REGISTER,
diskExtension->ReadCommand );
break;
}
case IRP_MJ_DEVICE_CONTROL: {
//
// The only way we can get this major code is if the VERIFY
// ioctl called AtDiskDispatchReadWrite().
//
if (!isSmartIoctl) {
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + COMMAND_REGISTER,
diskExtension->VerifyCommand );
}
break;
}
case IRP_MJ_WRITE: {
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + WRITE_PRECOMP_REGISTER,
diskExtension->WritePrecomp );
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + COMMAND_REGISTER,
diskExtension->WriteCommand );
//
// We can't put data in the controller cache until it's ready
// (it will turn off the BUSY_STATUS bit and then assert
// DATA_REQUEST_STATUS; there's no interrupt, so we must wait
// for that bit). That may take a long time on systems with
// power saving features, but most systems should be a heck of
// a lot faster.
//
ntStatus = AtWaitControllerReady( controllerExtension, 10, 5000 );
//
// Now wait for DATA_REQUEST_STATUS.
//
if ( NT_SUCCESS( ntStatus ) ) {
while ( ( !( READ_CONTROLLER(
controllerExtension->ControllerAddress + STATUS_REGISTER ) &
DATA_REQUEST_STATUS ) ) &&
( loopCount++ < 5000 ) ) {
//
// Wait for 10us each time; 5000 times will be 50ms.
//
KeStallExecutionProcessor( 10L );
}
if ( loopCount >= 5000 ) {
AtDump(
ATERRORS,
( "AtDisk ERROR: Controller not setting DRQ\n" ));
}
//
// We might have timed out. Blast ahead anyway - the hardware
// won't interrupt since it won't get all of this information,
// so we'll get a time-out since the timer is running.
//
// Copy the data to the cache. Update the user's address.
//
ASSERT(diskExtension->BytesPerInterrupt == 512);
if ( loopCount < 5000 ) {
WRITE_CONTROLLER_BUFFER(
controllerExtension->ControllerAddress + DATA_REGISTER,
diskExtension->CurrentAddress,
diskExtension->BytesPerInterrupt );
diskExtension->CurrentAddress +=
diskExtension->BytesPerInterrupt;
}
}
break;
} // IRP_MJ_WRITE
default: {
//
// We should never get here; READ or WRITE or VERIFY (via the
// ioctl interface) should be the only possibilities.
//
AtDump(
ATBUGCHECK,
("AtDisk ERROR: invalid operation type %x\n",
diskExtension->OperationType ));
}
}
return TRUE;
}
BOOLEAN
AtDiskInterruptService(
IN PKINTERRUPT Interrupt,
IN OUT PVOID Context
)
/*++
Routine Description:
This routine is called at DIRQL by the system when the controller
interrupts.
When this routine is called, the timer is running and the controller
object is owned (except at init time, but the DPC won't be invoked
so it doesn't matter).
It simply reads the status port to keep the controller from
interrupting again, turns off the timer, and (if necessary) queues a
DPC to the real work.
This routine is protected by device queue serialization and by DIRQL
on the current processor and the interrupt spinlock for other
processors. So it can change variables in both the device extension
and the controller extension and touch the hardware as much as it
wants.
Arguments:
Interrupt - a pointer to the interrupt object.
Context - a pointer to the controller extension (set up by the call
to IoConnectInterrupt in AtInitializeController() ).
Return Value:
Normally returns TRUE, but will return FALSE if this interrupt was
not expected.
--*/
{
PCONTROLLER_EXTENSION controllerExtension;
PDEVICE_OBJECT whichDeviceObject;
PDISK_EXTENSION diskExtension;
PIO_STACK_LOCATION irpSp;
UCHAR controllerStatus;
BOOLEAN isSmartIoctl = FALSE;
UNREFERENCED_PARAMETER( Interrupt );
controllerExtension = Context;
//
// Read the controller's status port, which will stop it from interrupting.
// We do this before checking to see whether the interrupt belongs to us
// or not, since the hardware might have had a glitch that caused a
// spurious interrupt.
//
controllerStatus = READ_CONTROLLER(
controllerExtension->ControllerAddress + STATUS_REGISTER );
//
// If the interrupt doesn't belong to us (either we weren't expecting
// one, or we were expecting one but our controller is still busy and
// not yet ready to interrupt, or we haven't finished initializing the
// device), return FALSE immediately so the next device in the chain
// can claim it.
//
if ( ( controllerExtension->InterruptTimer == CANCEL_TIMER ) ||
( controllerStatus & BUSY_STATUS ) ||
( !controllerExtension->WhichDeviceObject) ) {
return FALSE;
}
//
// We just got the interrupt we expected, so clear the timer.
//
controllerExtension->InterruptTimer = CANCEL_TIMER;
whichDeviceObject = controllerExtension->WhichDeviceObject;
controllerExtension->WhichDeviceObject = NULL;
diskExtension = whichDeviceObject->DeviceExtension;
if ( ( diskExtension->OperationType == IRP_MJ_DEVICE_CONTROL ) &&
( controllerExtension->ResettingController == RESET_NOT_RESETTING ) ) {
ULONG controlCode;
irpSp = IoGetCurrentIrpStackLocation(whichDeviceObject->CurrentIrp);
controlCode = irpSp->Parameters.DeviceIoControl.IoControlCode;
if ((controlCode == SMART_GET_VERSION) ||
(controlCode == SMART_SEND_DRIVE_COMMAND) ||
(controlCode == SMART_RCV_DRIVE_DATA)) {
isSmartIoctl = TRUE;
} else {
//
// The VERIFY command only gives one interrupt per transfer,
// rather than one interrupt per sector. Adjust the transfer
// length so that the DPC will end the transfer, and adjust
// the request length so that the DPC will end up reducing
// the request length by the total transfer length.
//
ASSERT(diskExtension->BytesPerInterrupt == 512);
diskExtension->RemainingRequestLength -=
( diskExtension->RemainingTransferLength -
diskExtension->BytesPerInterrupt );
diskExtension->RemainingTransferLength =
diskExtension->BytesPerInterrupt;
}
}
//
// Queue the DPC to actually do the work, if needed. Be sure to pass
// the device object that caused the interrupt.
//
if ( controllerExtension->InterruptRequiresDpc ) {
controllerExtension->InterruptRequiresDpc = FALSE;
IoRequestDpc(
whichDeviceObject,
whichDeviceObject->CurrentIrp,
( PVOID )controllerStatus );
}
return TRUE;
}
VOID
AtFinishPacket(
IN PDISK_EXTENSION DiskExtension,
IN NTSTATUS NtStatus
)
/*++
Routine Description:
All IRPs that aren't finished in a dispatch routine (that is, those
that go through the StartIo interface) should be finished via this
routine. The next packet (in C-SCAN order, sorted by starting
sector number) will be started, the IoStatus is set to the value
passed in, and the IRP is completed.
Arguments:
DiskExtension - a pointer to the device extension of the disk that
just completed an operation.
NtStatus - the status to complete the IRP with.
Return Value:
None.
--*/
{
PIRP irp = DiskExtension->DeviceObject->CurrentIrp;
ULONG firstSectorOfRequest;
//
// Need to pass current cylinder to ISNP so that it knows which
// packet to start next for C-SCAN.
//
firstSectorOfRequest = DiskExtension->FirstSectorOfRequest;
DiskExtension->FirstSectorOfRequest = MAXULONG;
IoStartNextPacketByKey(
DiskExtension->DeviceObject,
FALSE,
firstSectorOfRequest );
irp->IoStatus.Status = NtStatus;
AtDump(
ATDIAG2,
(
"ATDISK: Finishing Request of Irp: %x\n",
irp
) );
//
// For completing read requests we need to flush the io
// buffers.
//
if (IoGetCurrentIrpStackLocation(irp)->MajorFunction ==
IRP_MJ_READ) {
KeFlushIoBuffers(
irp->MdlAddress,
TRUE,
FALSE
);
}
IoCompleteRequest( irp, IO_DISK_INCREMENT );
}
VOID
AtDiskDeferredProcedure(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
/*++
Routine Description:
This routine is called at DISPATCH_LEVEL by the system at the
request of AtDiskInterruptService().
When this routine is called, the controller object is owned so this
procedure can change controller variables and touch the hardware.
This routine is protected by device queue serialization, so it can
change variables in the device extension. It is protected against
interrupts since it was queued by an interrupt, and the next
interrupt won't occur until something else happens (if there's a
spurious interrupt, the ISR will return without queuing this DPC or
changing any variables).
This routine can cause another interrupt by reprogramming the
controller, or by filling or emptying the cache. This could cause
us to enter AtDiskInterruptService() and even to reenter this
procedure, so after either of those operations variable usage should
be (and is) avoided.
This routine performs a number of functions - it helps reset the
drive if there was a time-out, it logs errors, it transfers data to
and from the controller cache, and it completes I/O operations and
gets new ones started.
Arguments:
Dpc - a pointer to the DPC object used to invoke this routine.
DeferredContext - a pointer to the device object associated with this
DPC.
SystemArgument1 - Actually a pointer to the current irp. We
don't actually use this, as we get it directly from our own
structures.
SystemArgument2 - the controller status byte, passed to us by
AtDiskInterruptService().
Return Value:
None.
--*/
{
PDEVICE_OBJECT deviceObject;
PDISK_EXTENSION diskExtension;
PCONTROLLER_EXTENSION controllerExtension;
PIRP irp; // save current IRP for IoCompleteRequest
PIO_STACK_LOCATION irpSp;
NTSTATUS returnStatus; // SUCCESS unless controller error
UCHAR controllerStatus;
BOOLEAN expectingAnInterrupt = FALSE; // TRUE iff we fill/empty the cache
BOOLEAN logTheError = TRUE;
BOOLEAN isSmartIoctl = FALSE;
ULONG loopCount = 0;
UCHAR codeValue;
//
// We capture into the following controller values for logging when
// there is an error.
//
UCHAR errorReg;
UCHAR driveHead;
UCHAR cylHigh;
UCHAR cylLow;
UCHAR sectorNumber;
UCHAR sectorCount;
UNREFERENCED_PARAMETER( Dpc );
UNREFERENCED_PARAMETER( SystemArgument1 );
deviceObject = DeferredContext;
diskExtension = deviceObject->DeviceExtension;
controllerExtension = diskExtension->ControllerExtension;
ASSERT(controllerExtension->InterruptTimer == CANCEL_TIMER);
//
// We will start another IRP before we complete the current one to
// maximize overlap, so now that we've decided which disk we're working
// on, let's get a pointer to the current IRP. Note that it could
// be NULL if the controller is being reset.
//
controllerStatus = ( UCHAR ) SystemArgument2;
//
// If there is some kind of error (or correctable error) then capture the
// register state of the controller
//
if (controllerStatus & ERROR_STATUS) {
errorReg = READ_CONTROLLER( controllerExtension->ControllerAddress +
ERROR_REGISTER );
driveHead = READ_CONTROLLER( controllerExtension->ControllerAddress +
DRIVE_HEAD_REGISTER );
cylHigh = READ_CONTROLLER( controllerExtension->ControllerAddress +
CYLINDER_HIGH_REGISTER );
cylLow = READ_CONTROLLER( controllerExtension->ControllerAddress +
CYLINDER_LOW_REGISTER ),
sectorNumber = READ_CONTROLLER( controllerExtension->ControllerAddress +
SECTOR_NUMBER_REGISTER );
sectorCount = READ_CONTROLLER( controllerExtension->ControllerAddress +
SECTOR_COUNT_REGISTER );
AtDump(
ATERRORS,
("ATDISK: Error regs that will be given to log\n"
" Error Register: %x\n"
" Drive Head: %x\n"
" Cylinder High: %x\n"
" Cylinder Low: %x\n"
" Sector Number: %x\n"
" Sector Count: %x\n"
" Status: %x\n",
errorReg,
driveHead,
cylHigh,
cylLow,
sectorNumber,
sectorCount,
controllerStatus)
);
}
irp = deviceObject->CurrentIrp;
if ( irp != NULL ) {
irpSp = IoGetCurrentIrpStackLocation( irp );
if (irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
ULONG controlCode = irpSp->Parameters.DeviceIoControl.IoControlCode;
if ((controlCode == SMART_GET_VERSION) ||
(controlCode == SMART_SEND_DRIVE_COMMAND) ||
(controlCode == SMART_RCV_DRIVE_DATA)) {
isSmartIoctl = TRUE;
}
}
}
//
// If a timer expired, the controller was reset. We may have to set
// drive parameters for a second drive, and/or restart an IRP.
//
switch ( controllerExtension->ResettingController ) {
case RESET_NOT_RESETTING: {
//
// Things are normal; no resetting going on.
//
break;
}
case RESET_FIRST_DRIVE_SET: {
//
// The controller has just been reset, and the failing drive's
// parameters have been set.
//
// Log an error. We couldn't do it in AtCheckTimerSync()
// because that runs at DIRQL.
//
AtDump(
ATERRORS,
("ATDISK: Reset the first drive\n")
);
//
// Nothing should have failed at this point but it
// doesn't hurt to check.
//
if (controllerStatus & ERROR_STATUS) {
AtLogError(
deviceObject,
0,
0,
0,
7,
STATUS_SUCCESS,
IO_ERR_CONTROLLER_ERROR,
ERROR_LOG_TYPE_ERROR,
errorReg,
driveHead,
cylHigh,
cylLow,
sectorNumber,
sectorCount,
controllerStatus
);
logTheError = FALSE;
goto ResetCodePath;
} else {
AtLogError(
deviceObject,
((irp)?(diskExtension->SequenceNumber):(0)),
(UCHAR)((irp)?(irpSp->MajorFunction):(0)),
diskExtension->IrpRetryCount,
1,
STATUS_SUCCESS,
IO_ERR_RESET,
ERROR_LOG_TYPE_TIMEOUT,
0,
0,
0,
0,
0,
0,
0);
}
//
// This is along the reset path. The ATA protocols state
// that the only thing that should have happened here
// is that the busy bit got turned off by the status register
// read. We wouldn't have gotten here if busy was still set
// (The ISR would simply dismiss the interrupt without queueing
// us.)
//
// In any case give the poor disk a little time to calm down.
//
KeStallExecutionProcessor(25);
//
// Now recalibrate the first (failing) drive.
//
controllerExtension->ResettingController =
RESET_FIRST_DRIVE_RECALIBRATED;
controllerExtension->InterruptRequiresDpc = TRUE;
controllerExtension->WhichDeviceObject =
diskExtension->DeviceObject;
AtDump(
ATERRORS,
("ATDISK: About to recalibrate controller for ext: %x\n"
" with a device unit of: %x\n",
diskExtension,
diskExtension->DeviceUnit)
);
controllerExtension->InterruptTimer = START_TIMER_FOR_RECALIBRATE;
( VOID ) AtWaitControllerReady( controllerExtension, 20, 150000 );
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + DRIVE_HEAD_REGISTER,
diskExtension->DeviceUnit
);
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + COMMAND_REGISTER,
RECALIBRATE_COMMAND );
return;
}
case RESET_FIRST_DRIVE_RECALIBRATED: {
AtDump(
ATERRORS,
("ATDISK: Recalibrated the first drive\n")
);
//
// The only thing that could have gone wrong at this
// point (at least according to ATA is that we couldn't
// seek to track 0. Check the status first before we
// go on.
//
if (controllerStatus & ERROR_STATUS) {
AtLogError(
deviceObject,
0,
0,
0,
8,
STATUS_SUCCESS,
IO_ERR_SEEK_ERROR,
ERROR_LOG_TYPE_ERROR,
errorReg,
driveHead,
cylHigh,
cylLow,
sectorNumber,
sectorCount,
controllerStatus
);
logTheError = FALSE;
goto ResetCodePath;
}
if ( controllerExtension->Disk2 == NULL ) {
//
// There's only one drive, and it's ready to go.
//
// We need to restart the packet that was being worked
// on when we timed out. We can't release the controller
// until after we've cleared ResettingController. This
// ensures that we won't get any interrupts (from
// restarting the I/O) until we're ready to leave this
// routine.
//
if ( irp != NULL ) {
AtDump(
ATERRORS,
(
"ATDISK: In first controller one drive restart\n"
" Irp of Request: %x\n"
" Starting vmem Address of Transfer: %x\n"
" Ending vmem Address of Transfer: %x\n"
" Length of Transfer: %x\n",
irp,
diskExtension->CurrentAddress,
((PUCHAR)diskExtension->CurrentAddress)+diskExtension->RemainingRequestLength,
diskExtension->RemainingRequestLength
) );
AtDump(
ATERRORS,
("ATDISK - Sector number is: %x\n"
" CHS: %x-%x-%x\n",
diskExtension->FirstSectorOfTransfer,
diskExtension->FirstSectorOfTransfer /
(diskExtension->SectorsPerTrack *
diskExtension->TracksPerCylinder),
(diskExtension->FirstSectorOfTransfer /
diskExtension->SectorsPerTrack) %
diskExtension->TracksPerCylinder,
diskExtension->FirstSectorOfTransfer %
diskExtension->TracksPerCylinder
) );
//
// If the disk operation was a verify then don't
// retry. We want this sector to be marked bad
// if verify had a problem with it.
//
if ( (diskExtension->IrpRetryCount <
RETRY_IRP_MAXIMUM_COUNT) &&
(irpSp->MajorFunction != IRP_MJ_DEVICE_CONTROL) ) {
diskExtension->PacketIsBeingRetried = TRUE;
diskExtension->FirstSectorOfRequest = MAXULONG;
//
// This is along the reset path. This shouldn't
// happen too often. Let's give the poor disk
// a little time to calm down before we hit
// it with another request.
//
KeStallExecutionProcessor(25);
AtDiskStartIo( deviceObject, irp );
} else {
//
// We've retried too many times. Just return with
// failure.
//
AtDump(
ATERRORS,
("AtDisk ERROR: too many retries 1; will fail IRP\n" ));
AtFinishPacket( diskExtension, STATUS_DISK_RECALIBRATE_FAILED );
}
}
diskExtension->ControllerExtension->ResettingController =
RESET_NOT_RESETTING;
IoFreeController( controllerExtension->ControllerObject );
} else {
//
// There are two drives; we've just reset the one that
// timed out. Now set the drive params of the other one.
//
// Note that we are about to alter InterruptRequiresDpc,
// ResettingController and InterruptTimer, and we are about
// to write to the hardware. AtCheckTimerSync() and
// AtDiskInterruptService() touch these items, so
// generally we'd say we have to do the following from a
// routine that has been KeSync'd. But it is not necessary
// in this case since no activity is expected - no interrupts
// are currently expected (we've only issued one command since
// resetting the controller, and it has already interrupted),
// so the ISR won't run (even if it does, it will exit
// without doing anything); and further, since InterruptTimer
// isn't set (it was cleared in the ISR) the timer routine
// won't run either. The other disk is waiting for the
// controller object. This operation will cause an
// interrupt, but we exit immediately after causing it
// so there will be no contention problems.
//
controllerExtension->ResettingController =
RESET_SECOND_DRIVE_SET;
controllerExtension->InterruptRequiresDpc = TRUE;
controllerExtension->WhichDeviceObject =
diskExtension->OtherDiskExtension->DeviceObject;
//
// This is along the reset path. This shouldn't
// happen too often. Let's give the poor disk
// a little time to calm down before we hit
// it with another request.
//
KeStallExecutionProcessor(25);
( VOID ) AtWaitControllerReady( controllerExtension, 20, 150000 );
WRITE_CONTROLLER(
controllerExtension->ControllerAddress +
DRIVE_HEAD_REGISTER,
( diskExtension->OtherDiskExtension->DeviceUnit |
( diskExtension->OtherDiskExtension->TracksPerCylinder
- 1 ) ) );
WRITE_CONTROLLER(
controllerExtension->ControllerAddress +
SECTOR_COUNT_REGISTER,
diskExtension->OtherDiskExtension->SectorsPerTrack );
AtDump(
ATERRORS,
("ATDISK: About to set parms controller for ext: %x\n"
" with a device unit of: %x\n",
diskExtension->OtherDiskExtension,
diskExtension->OtherDiskExtension->DeviceUnit)
);
controllerExtension->InterruptTimer = START_TIMER;
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + COMMAND_REGISTER,
SET_DRIVE_PARAMETERS_COMMAND );
}
return;
}
case RESET_SECOND_DRIVE_SET: {
AtDump(
ATERRORS,
("ATDISK: Reset second drive\n")
);
//
// Nothing should have failed at this point but it
// doesn't hurt to check.
//
if (controllerStatus & ERROR_STATUS) {
AtDump(
ATERRORS,
("ATDISK: Error from controller for non-fail parm set - stat: %x\n",
controllerStatus)
);
AtLogError(
deviceObject,
0,
0,
0,
9,
STATUS_SUCCESS,
IO_ERR_CONTROLLER_ERROR,
ERROR_LOG_TYPE_ERROR,
errorReg,
driveHead,
cylHigh,
cylLow,
sectorNumber,
sectorCount,
controllerStatus
);
logTheError = FALSE;
goto ResetCodePath;
} else {
AtLogError(
deviceObject,
((irp)?(diskExtension->SequenceNumber):(0)),
(UCHAR)((irp)?(irpSp->MajorFunction):(0)),
diskExtension->IrpRetryCount,
10,
STATUS_SUCCESS,
IO_ERR_RESET,
ERROR_LOG_TYPE_TIMEOUT,
0,
0,
0,
0,
0,
0,
0);
}
//
// Now recalibrate the second (ie nonfailing) drive.
//
controllerExtension->ResettingController =
RESET_SECOND_DRIVE_RECALIBRATED;
controllerExtension->InterruptRequiresDpc = TRUE;
controllerExtension->WhichDeviceObject =
diskExtension->DeviceObject;
AtDump(
ATERRORS,
("ATDISK: About to recal for non-failing for ext: %x\n"
" with a device unit of: %x\n",
diskExtension,
diskExtension->DeviceUnit)
);
//
// This is along the reset path. This shouldn't
// happen too often. Let's give the poor disk
// a little time to calm down before we hit
// it with another request.
//
KeStallExecutionProcessor(25);
( VOID ) AtWaitControllerReady( controllerExtension, 20, 150000 );
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + DRIVE_HEAD_REGISTER,
diskExtension->DeviceUnit
);
controllerExtension->InterruptTimer = START_TIMER_FOR_RECALIBRATE;
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + COMMAND_REGISTER,
RECALIBRATE_COMMAND );
return;
}
case RESET_SECOND_DRIVE_RECALIBRATED: {
AtDump(
ATERRORS,
("ATDISK: recalibrated second drive\n")
);
//
// The only thing that could have gone wrong at this
// point (at least according to ATA) is that we couldn't
// seek to track 0. Check the status first before we
// go on.
//
if (controllerStatus & ERROR_STATUS) {
AtDump(
ATERRORS,
("ATDISK: Error from controller for non-fail recal - stat: %x\n",
controllerStatus)
);
AtLogError(
deviceObject,
0,
0,
0,
11,
STATUS_SUCCESS,
IO_ERR_SEEK_ERROR,
ERROR_LOG_TYPE_ERROR,
errorReg,
driveHead,
cylHigh,
cylLow,
sectorNumber,
sectorCount,
controllerStatus
);
logTheError = FALSE;
goto ResetCodePath;
}
//
// We've reset and recalibrated both drives. So
// now restart the packet that timed out, which was on the OTHER
// drive. Don't clear ResettingController until after we've
// called AtDiskStartIo(), so it knows not to remap the user's
// buffer. And we can't release the controller until after
// we've cleared ResettingController. This also ensures that
// we won't get any interrupts (from restarting the I/O) until
// we're ready to leave this routine.
//
diskExtension = diskExtension->OtherDiskExtension;
irp = diskExtension->DeviceObject->CurrentIrp;
if ( irp != NULL ) {
irpSp = IoGetCurrentIrpStackLocation( irp );
AtDump(
ATERRORS,
(
"ATDISK: In controller two drive restart\n"
" Irp of Request: %x\n"
" Starting vmem Address of Transfer: %x\n"
" Ending vmem Address of Transfer: %x\n"
" Length of Transfer: %x\n",
irp,
diskExtension->CurrentAddress,
((PUCHAR)diskExtension->CurrentAddress)+diskExtension->RemainingRequestLength,
diskExtension->RemainingRequestLength
) );
AtDump(
ATERRORS,
("ATDISK - Sector number is: %x\n"
" CHS: %x-%x-%x\n",
diskExtension->FirstSectorOfTransfer,
diskExtension->FirstSectorOfTransfer /
(diskExtension->SectorsPerTrack *
diskExtension->TracksPerCylinder),
(diskExtension->FirstSectorOfTransfer /
diskExtension->SectorsPerTrack) %
diskExtension->TracksPerCylinder,
diskExtension->FirstSectorOfTransfer %
diskExtension->TracksPerCylinder
) );
//
// If the disk operation was a verify then don't
// retry. We want this sector to be marked bad
// if verify had a problem with it.
//
if ( (diskExtension->IrpRetryCount <
RETRY_IRP_MAXIMUM_COUNT) &&
(irpSp->MajorFunction != IRP_MJ_DEVICE_CONTROL) ) {
diskExtension->PacketIsBeingRetried = TRUE;
diskExtension->FirstSectorOfRequest = MAXULONG;
//
// This is along the reset path. This shouldn't
// happen too often. Let's give the poor disk
// a little time to calm down before we hit
// it with another request.
//
KeStallExecutionProcessor(25);
AtDiskStartIo(
diskExtension->DeviceObject,
irp );
} else {
AtDump(
ATERRORS,
("AtDisk ERROR: too many retries 2; will fail IRP\n" ));
AtFinishPacket( diskExtension, STATUS_DISK_RECALIBRATE_FAILED );
}
}
diskExtension->ControllerExtension->ResettingController =
RESET_NOT_RESETTING;
IoFreeController( controllerExtension->ControllerObject );
return;
}
}
//
// We want to update the remaining length and fill/empty the cache.
// But if there was an error we don't want to update the remaining
// length, even though we do have to fill/empty the cache.
//
if ( !( controllerStatus & ERROR_STATUS ) ) {
ASSERT(diskExtension->BytesPerInterrupt == 512);
if (!isSmartIoctl) {
if ((!diskExtension->RemainingTransferLength) ||
(!diskExtension->RemainingRequestLength)) {
AtLogError(
deviceObject,
((irp)?(diskExtension->SequenceNumber):(0)),
(UCHAR)((irp)?(irpSp->MajorFunction):(0)),
diskExtension->IrpRetryCount,
12,
STATUS_SUCCESS,
IO_ERR_DRIVER_ERROR,
ERROR_LOG_TYPE_ERROR,
errorReg,
driveHead,
cylHigh,
cylLow,
sectorNumber,
sectorCount,
controllerStatus );
logTheError = FALSE;
controllerStatus |= ERROR_STATUS;
goto ResetCodePath;
}
diskExtension->RemainingTransferLength -=
diskExtension->BytesPerInterrupt;
diskExtension->RemainingRequestLength -=
diskExtension->BytesPerInterrupt;
} else {
if (irpSp->Parameters.DeviceIoControl.IoControlCode == SMART_RCV_DRIVE_DATA) {
diskExtension->RemainingTransferLength = 0;
diskExtension->RemainingRequestLength = 0;
}
}
//
// If there's more to the current transfer, servicing the
// controller cache will cause it to start the next piece,
// causing an interrupt.
//
if ( diskExtension->RemainingTransferLength > 0 ) {
//
// Note that we are about to alter InterruptRequiresDpc and
// InterruptTimer. These are altered by
// AtDiskInterruptService() and AtCheckTimerSync(), so
// generally we'd say we have to do the following from a
// routine that has been KeSync'd. But it is not necessary in
// this case since no activity is expected - no interrupts are
// currently expected (not until we empty or fill the buffer,
// below), so the ISR won't run; and since InterruptTimer isn't
// set (since it was cleared in the ISR) the timer routine
// won't run either.
//
controllerExtension->InterruptRequiresDpc = TRUE;
controllerExtension->WhichDeviceObject =
diskExtension->DeviceObject;
controllerExtension->InterruptTimer = START_TIMER;
expectingAnInterrupt = TRUE;
}
}
//
// If this is a READ, we must empty the cache whether there was
// an error or not. For WRITEs, we similarly must fill the cache
// as long as there is more work to be done. Note that it is safe
// to touch the hardware here since the controller won't do
// anything until the buffer has been filled or emptied - so the
// ISR won't run, and the timer won't touch the hardware for at
// least another second (when the timer expires).
//
switch ( diskExtension->OperationType ) {
case IRP_MJ_DEVICE_CONTROL: {
PVOID BufferToReadTo;
ULONG controlCode = irpSp->Parameters.DeviceIoControl.IoControlCode;
if ((controlCode == SMART_GET_VERSION) ||
(controlCode == SMART_SEND_DRIVE_COMMAND) ||
(controlCode == SMART_RCV_DRIVE_DATA)) {
//
// Extract the code from the IOCTL value. Used in case of errors.
//
codeValue = (UCHAR)((controlCode >> 2) & 0xFF);
//
// If this was an enable or disable, there is no data to transfer.
// Simply break and finish up processing of the request.
//
if (controlCode == SMART_SEND_DRIVE_COMMAND) {
break;
}
ASSERT(diskExtension->BytesPerInterrupt == 512);
if ( !( controllerStatus & ERROR_STATUS ) ) {
BufferToReadTo = diskExtension->CurrentAddress;
BufferToReadTo = ((PSENDCMDOUTPARAMS)BufferToReadTo)->bBuffer;
diskExtension->CurrentAddress +=
diskExtension->BytesPerInterrupt;
} else {
BufferToReadTo = &controllerExtension->GarbageCan[0];
}
//
// Wait for DRQ assertion if necessary.
//
while ( ( !( READ_CONTROLLER(
controllerExtension->ControllerAddress + STATUS_REGISTER ) &
DATA_REQUEST_STATUS ) ) &&
( loopCount++ < 5000 ) ) {
//
// Wait for 10us each time; 5000 times will be 50ms.
//
KeStallExecutionProcessor( 10L );
}
if ( loopCount >= 5000 ) {
AtDump(
ATERRORS,
( "AtDisk ERROR: Controller not setting DRQ\n" ));
controllerStatus &= ERROR_STATUS;
AtLogError(
deviceObject,
((irp)?(diskExtension->SequenceNumber):(0)),
(UCHAR)((irp)?(irpSp->MajorFunction):(0)),
diskExtension->IrpRetryCount,
20,
STATUS_SUCCESS,
IO_ERR_CONTROLLER_ERROR,
ERROR_LOG_TYPE_ERROR,
errorReg,
driveHead,
cylHigh,
cylLow,
0,
codeValue,
controllerStatus );
logTheError = FALSE;
controllerStatus |= ERROR_STATUS;
goto ResetCodePath;
}
//
// Some broken PCI adapters cause us to do the io
// with int's "disabled".
//
if (controllerExtension->BadPciAdapter) {
BAD_PCI_BLOCK context;
context.DiskExtension = diskExtension;
context.Buffer = BufferToReadTo;
KeSynchronizeExecution(
controllerExtension->InterruptObject,
AtDiskReadWithSync,
&context
);
} else {
READ_CONTROLLER_BUFFER(
controllerExtension->ControllerAddress + DATA_REGISTER,
BufferToReadTo,
diskExtension->BytesPerInterrupt
);
}
if (NT_SUCCESS(AtWaitControllerReady(controllerExtension,
10,
5000
))) {
if (READ_CONTROLLER(
controllerExtension->ControllerAddress + STATUS_REGISTER ) &
DATA_REQUEST_STATUS ) {
AtLogError(
deviceObject,
((irp)?(diskExtension->SequenceNumber):(0)),
(UCHAR)((irp)?(irpSp->MajorFunction):(0)),
diskExtension->IrpRetryCount,
21,
STATUS_SUCCESS,
IO_ERR_OVERRUN_ERROR,
ERROR_LOG_TYPE_ERROR,
errorReg,
driveHead,
cylHigh,
cylLow,
0,
codeValue,
controllerStatus );
logTheError = FALSE;
controllerStatus |= ERROR_STATUS;
goto ResetCodePath;
}
} else {
AtLogError(
deviceObject,
((irp)?(diskExtension->SequenceNumber):(0)),
(UCHAR)((irp)?(irpSp->MajorFunction):(0)),
diskExtension->IrpRetryCount,
22,
STATUS_SUCCESS,
IO_ERR_CONTROLLER_ERROR,
ERROR_LOG_TYPE_ERROR,
errorReg,
driveHead,
cylHigh,
cylLow,
0,
codeValue,
controllerStatus );
logTheError = FALSE;
controllerStatus |= ERROR_STATUS;
goto ResetCodePath;
}
break;
} else {
//
// This is a VERIFY ioctl. There's nothing to do.
//
break;
}
}
case IRP_MJ_READ: {
PVOID BufferToReadTo;
BOOLEAN CheckDrq = FALSE;
if (!diskExtension->RemainingTransferLength) {
//
// We want to make sure that the DRQ is low when
// we think we are all done with the IO.
//
CheckDrq = TRUE;
}
//
// Get the data that was just read, and update our position in
// the user's buffer. Update before reading the cache, since
// reading the buffer could cause an interrupt to come immediately.
// After emptying the cache, we cannot access the hardware or
// variables since AtDiskInterruptService() or even this routine
// could be trying to access them.
//
// In case of an error we won't update the current address.
//
ASSERT(diskExtension->BytesPerInterrupt == 512);
if ( !( controllerStatus & ERROR_STATUS ) ) {
BufferToReadTo = diskExtension->CurrentAddress;
diskExtension->CurrentAddress +=
diskExtension->BytesPerInterrupt;
} else {
BufferToReadTo = &controllerExtension->GarbageCan[0];
}
//
// Wait for DRQ assertion.
//
while ( ( !( READ_CONTROLLER(
controllerExtension->ControllerAddress + STATUS_REGISTER ) &
DATA_REQUEST_STATUS ) ) &&
( loopCount++ < 5000 ) ) {
//
// Wait for 10us each time; 5000 times will be 50ms.
//
KeStallExecutionProcessor( 10L );
}
if ( loopCount >= 5000 ) {
AtDump(
ATERRORS,
( "AtDisk ERROR: Controller not setting DRQ\n" ));
controllerStatus &= ERROR_STATUS;
AtLogError(
deviceObject,
((irp)?(diskExtension->SequenceNumber):(0)),
(UCHAR)((irp)?(irpSp->MajorFunction):(0)),
diskExtension->IrpRetryCount,
13,
STATUS_SUCCESS,
IO_ERR_CONTROLLER_ERROR,
ERROR_LOG_TYPE_ERROR,
errorReg,
driveHead,
cylHigh,
cylLow,
sectorNumber,
sectorCount,
controllerStatus );
logTheError = FALSE;
controllerStatus |= ERROR_STATUS;
goto ResetCodePath;
}
//
// Some broken PCI adapters cause us to do the io
// with int's "disabled".
//
if (controllerExtension->BadPciAdapter) {
BAD_PCI_BLOCK context;
context.DiskExtension = diskExtension;
context.Buffer = BufferToReadTo;
KeSynchronizeExecution(
controllerExtension->InterruptObject,
AtDiskReadWithSync,
&context
);
} else {
READ_CONTROLLER_BUFFER(
controllerExtension->ControllerAddress + DATA_REGISTER,
BufferToReadTo,
diskExtension->BytesPerInterrupt
);
}
if (CheckDrq) {
if (NT_SUCCESS(AtWaitControllerReady(controllerExtension,
10,
5000
))) {
if (READ_CONTROLLER(
controllerExtension->ControllerAddress + STATUS_REGISTER ) &
DATA_REQUEST_STATUS ) {
AtLogError(
deviceObject,
((irp)?(diskExtension->SequenceNumber):(0)),
(UCHAR)((irp)?(irpSp->MajorFunction):(0)),
diskExtension->IrpRetryCount,
14,
STATUS_SUCCESS,
IO_ERR_OVERRUN_ERROR,
ERROR_LOG_TYPE_ERROR,
errorReg,
driveHead,
cylHigh,
cylLow,
sectorNumber,
sectorCount,
controllerStatus );
logTheError = FALSE;
controllerStatus |= ERROR_STATUS;
goto ResetCodePath;
}
} else {
AtLogError(
deviceObject,
((irp)?(diskExtension->SequenceNumber):(0)),
(UCHAR)((irp)?(irpSp->MajorFunction):(0)),
diskExtension->IrpRetryCount,
15,
STATUS_SUCCESS,
IO_ERR_CONTROLLER_ERROR,
ERROR_LOG_TYPE_ERROR,
errorReg,
driveHead,
cylHigh,
cylLow,
sectorNumber,
sectorCount,
controllerStatus );
logTheError = FALSE;
controllerStatus |= ERROR_STATUS;
goto ResetCodePath;
}
}
break;
}
case IRP_MJ_WRITE: {
PVOID BufferToWriteFrom;
if (!diskExtension->RemainingTransferLength) {
if (NT_SUCCESS(AtWaitControllerReady(controllerExtension,
10,
5000
))) {
if (READ_CONTROLLER(
controllerExtension->ControllerAddress + STATUS_REGISTER ) &
DATA_REQUEST_STATUS ) {
AtLogError(
deviceObject,
((irp)?(diskExtension->SequenceNumber):(0)),
(UCHAR)((irp)?(irpSp->MajorFunction):(0)),
diskExtension->IrpRetryCount,
16,
STATUS_SUCCESS,
IO_ERR_OVERRUN_ERROR,
ERROR_LOG_TYPE_ERROR,
errorReg,
driveHead,
cylHigh,
cylLow,
sectorNumber,
sectorCount,
controllerStatus );
logTheError = FALSE;
controllerStatus |= ERROR_STATUS;
goto ResetCodePath;
}
} else {
AtLogError(
deviceObject,
((irp)?(diskExtension->SequenceNumber):(0)),
(UCHAR)((irp)?(irpSp->MajorFunction):(0)),
diskExtension->IrpRetryCount,
17,
STATUS_SUCCESS,
IO_ERR_CONTROLLER_ERROR,
ERROR_LOG_TYPE_ERROR,
errorReg,
driveHead,
cylHigh,
cylLow,
sectorNumber,
sectorCount,
controllerStatus );
logTheError = FALSE;
controllerStatus |= ERROR_STATUS;
goto ResetCodePath;
}
}
//
// IFF there is more to write, fill the sector cache and update
// our position in the user's buffer. Update before writing the
// cache, since writing the buffer could cause an interrupt to
// come immediately.
// After emptying the cache, we cannot access the hardware or
// variables since AtDiskInterruptService() or even this routine
// could be trying to access them.
//
if ( diskExtension->RemainingTransferLength > 0 ) {
ASSERT(diskExtension->BytesPerInterrupt == 512);
if ( !( controllerStatus & ERROR_STATUS ) ) {
BufferToWriteFrom = diskExtension->CurrentAddress;
diskExtension->CurrentAddress += diskExtension->BytesPerInterrupt;
} else {
BufferToWriteFrom = &controllerExtension->GarbageCan[0];
}
//
// Some broken PCI adapters cause us to do the io
// with int's "disabled".
//
if (controllerExtension->BadPciAdapter) {
BAD_PCI_BLOCK context;
context.DiskExtension = diskExtension;
context.Buffer = BufferToWriteFrom;
KeSynchronizeExecution(
controllerExtension->InterruptObject,
AtDiskWriteWithSync,
&context
);
} else {
WRITE_CONTROLLER_BUFFER(
controllerExtension->ControllerAddress + DATA_REGISTER,
BufferToWriteFrom,
diskExtension->BytesPerInterrupt );
}
}
break;
}
}
//
// If there was a correctable error, then log it. But operations
// should continue normally.
//
if ( controllerStatus & CORRECTED_ERROR_STATUS ) {
AtDump(
ATERRORS,
(
"ATDISK *******CORRECTABLE ERROR***********: Continueing Operation\n") );
AtLogError(
deviceObject,
((irp)?(diskExtension->SequenceNumber):(0)),
(UCHAR)((irp)?(irpSp->MajorFunction):(0)),
diskExtension->IrpRetryCount,
2,
STATUS_SUCCESS,
IO_ERR_RETRY_SUCCEEDED,
ERROR_LOG_TYPE_ERROR,
errorReg,
driveHead,
cylHigh,
cylLow,
sectorNumber,
sectorCount,
controllerStatus
);
}
//
// If there was a non-correctable error, then log it and make sure
// we return the error status.
//
ResetCodePath:;
returnStatus = STATUS_SUCCESS;
if ( controllerStatus & ERROR_STATUS ) {
AtDump(
ATERRORS,
(
"ATDISK: In deferred irp recovery\n"
" Irp of Request: %x\n"
" Starting vmem Address of Transfer: %x\n"
" Ending vmem Address of Transfer: %x\n"
" Length of Transfer: %x\n",
irp,
diskExtension->CurrentAddress,
((PUCHAR)diskExtension->CurrentAddress)+diskExtension->RemainingRequestLength,
diskExtension->RemainingRequestLength
) );
AtDump(
ATERRORS,
("ATDISK - Sector number is: %x\n"
" CHS: %x-%x-%x\n",
diskExtension->FirstSectorOfTransfer,
diskExtension->FirstSectorOfTransfer /
(diskExtension->SectorsPerTrack *
diskExtension->TracksPerCylinder),
(diskExtension->FirstSectorOfTransfer /
diskExtension->SectorsPerTrack) %
diskExtension->TracksPerCylinder,
diskExtension->FirstSectorOfTransfer %
diskExtension->TracksPerCylinder
) );
AtDump(
ATERRORS,
("AtDisk ******ERROR******: Disk error encountered - will retry %d more times.\n",RETRY_IRP_MAXIMUM_COUNT - diskExtension->IrpRetryCount));
//
// See if we've already exceeded the retry count on the
// irp. If we haven't then retry it. Otherwise, fail
// the request.
//
if ( diskExtension->IrpRetryCount < RETRY_IRP_MAXIMUM_COUNT ) {
if (logTheError) {
AtLogError(
deviceObject,
((irp)?(diskExtension->SequenceNumber):(0)),
(UCHAR)((irp)?(irpSp->MajorFunction):(0)),
diskExtension->IrpRetryCount,
4,
STATUS_SUCCESS,
IO_ERR_CONTROLLER_ERROR,
ERROR_LOG_TYPE_ERROR,
errorReg,
driveHead,
cylHigh,
cylLow,
sectorNumber,
sectorCount,
controllerStatus );
}
//
// Every time we get an error on the device, we
// might as well reset it. This will hopefully
// increase the chances that the io will succeed
// this time.
//
// NOTE NOTE: This next call will almost certainly
// cause an interrupt, and cause this
// DPC to be re-entered. We aren't
// doing anything after the call.
//
KeSynchronizeExecution(
controllerExtension->InterruptObject,
AtDiskStartReset,
diskExtension
);
return;
} else {
KdPrint(("ATDISK: Disk Controller still fails after %d retries - FAILING operation\n",RETRY_IRP_MAXIMUM_COUNT));
AtLogError(
deviceObject,
((irp)?(diskExtension->SequenceNumber):(0)),
(UCHAR)((irp)?(irpSp->MajorFunction):(0)),
diskExtension->IrpRetryCount,
5,
STATUS_DISK_OPERATION_FAILED,
IO_ERR_CONTROLLER_ERROR,
ERROR_LOG_TYPE_ERROR,
errorReg,
driveHead,
cylHigh,
cylLow,
sectorNumber,
sectorCount,
controllerStatus);
returnStatus = STATUS_DISK_OPERATION_FAILED;
}
}
//
// If we have finished the current transfer but there's more remaining
// in the request, then go program the controller to do the next
// transfer. Note that this code is only executed if we do NOT
// expect an interrupt due to cache emptying or filling; so we do not
// have to worry about contention with AtDiskInterruptService() or
// reentry to this routine.
//
if ( ( returnStatus == STATUS_SUCCESS ) &&
( !expectingAnInterrupt ) &&
( diskExtension->RemainingRequestLength != 0 ) ) {
//
// First calculate starting sector and length of the new transfer.
//
diskExtension->FirstSectorOfTransfer +=
diskExtension->TotalTransferLength >>
diskExtension->ByteShiftToSector;
//
// If possible, this transfer should be the same length as the
// request. However, it is limited to MAX_SEC_TO_TRANS sectors.
//
if ( diskExtension->RemainingRequestLength >
( ULONG )( diskExtension->BytesPerSector * MAX_SEC_TO_TRANS ) ) {
diskExtension->TotalTransferLength =
diskExtension->BytesPerSector * MAX_SEC_TO_TRANS;
} else {
diskExtension->TotalTransferLength =
diskExtension->RemainingRequestLength;
}
diskExtension->RemainingTransferLength =
diskExtension->TotalTransferLength;
//
// Give the poor disk some time to calm down between requests.
//
KeStallExecutionProcessor(100);
//
// No need to check whether TRUE or FALSE is returned, since we're
// going to return with no value in either case. If it didn't
// work due to power failure or a time-out, the controller reset
// code will restart the packet for us.
//
KeSynchronizeExecution(
controllerExtension->InterruptObject,
AtStartDevice,
diskExtension );
return;
} // transfer done, but request isn't
//
// If there was an error or the transfer is done, then
// set status and info
// deallocate controller
// start next packet
// complete I/O request
// Note that if we filled or emptied the cache and are expecting an
// interrupt, we will not execute this code. This way we do not have
// to worry about RemainingRequestLength having been changed by an
// instance of this routine queued by an interrupt we caused. (Note
// that filling or emptying the cache does NOT cause an interrupt if
// there was an error, which is why the IF statement checks for error
// before checking to see if we're expecting an interrupt).
//
if ( ( returnStatus == STATUS_DISK_OPERATION_FAILED ) ||
( !expectingAnInterrupt ) &&
( diskExtension->RemainingRequestLength == 0 ) ) {
if ( returnStatus == STATUS_SUCCESS ) {
//
// Success. Note that all of the data was transferred.
//
if (!isSmartIoctl) {
deviceObject->CurrentIrp->IoStatus.Information =
irpSp->Parameters.Read.Length;
} else {
ULONG controlCode = irpSp->Parameters.DeviceIoControl.IoControlCode;
//
// If this was a 'receive data command' update the Information field.
// otherwise it was enable/disable so should be the sizeof the output buffer.
//
if (controlCode == SMART_SEND_DRIVE_COMMAND) {
PSENDCMDINPARAMS cmdInParameters = ((PSENDCMDINPARAMS)irp->AssociatedIrp.SystemBuffer);
codeValue = cmdInParameters->irDriveRegs.bFeaturesReg;
if (codeValue == RETURN_SMART_STATUS) {
PSENDCMDOUTPARAMS cmdOutParameters = ((PSENDCMDOUTPARAMS)irp->AssociatedIrp.SystemBuffer);
PIDEREGS ideRegs = (PIDEREGS)cmdOutParameters->bBuffer;
deviceObject->CurrentIrp->IoStatus.Information =
sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDEREGS);
//
// Fill in the ide regs structure.
//
ideRegs->bFeaturesReg = RETURN_SMART_STATUS;
ideRegs->bSectorCountReg= READ_PORT_UCHAR(controllerExtension->ControllerAddress + SECTOR_COUNT_REGISTER);
ideRegs->bSectorNumberReg = READ_PORT_UCHAR(controllerExtension->ControllerAddress + SECTOR_NUMBER_REGISTER);
ideRegs->bCylLowReg= READ_PORT_UCHAR(controllerExtension->ControllerAddress + CYLINDER_LOW_REGISTER);
ideRegs->bCylHighReg= READ_PORT_UCHAR(controllerExtension->ControllerAddress + CYLINDER_HIGH_REGISTER);
ideRegs->bDriveHeadReg= READ_PORT_UCHAR(controllerExtension->ControllerAddress + DRIVE_HEAD_REGISTER);
ideRegs->bCommandReg= SMART_CMD;
} else {
deviceObject->CurrentIrp->IoStatus.Information = sizeof(SENDCMDOUTPARAMS) - 1;
}
} else {
deviceObject->CurrentIrp->IoStatus.Information = (512 + sizeof(SENDCMDOUTPARAMS) - 1);
}
}
} else {
//
// There was an error, so not all of the data was transferred.
// Let the user know how much data WAS transferred before the
// error. Variables like RemainingTransferLength might be
// incorrect, since controller buffering might mean that we
// didn't get an error on sector N until long after we've passed
// it. So instead, we add the amount moved in previous transfers
// of this request (the starting sector of the current transfer
// minus the original starting sector, all times bytes per sector)
// to the amount moved in the current transfer (the transfer length
// minus the length after the sector with the error).
//
deviceObject->CurrentIrp->IoStatus.Information =
((diskExtension->FirstSectorOfTransfer -
((ULONG)(irpSp->Parameters.Read.ByteOffset.QuadPart >>
diskExtension->ByteShiftToSector))) <<
diskExtension->ByteShiftToSector) +
( diskExtension->TotalTransferLength -
( READ_CONTROLLER( controllerExtension->ControllerAddress +
SECTOR_COUNT_REGISTER ) << diskExtension->ByteShiftToSector ) );
}
//
// Since this operation is done, the controller object can be
// released and the next operation can be started.
//
IoFreeController( controllerExtension->ControllerObject );
AtFinishPacket( diskExtension, returnStatus );
} // request done or error encountered
}
BOOLEAN
AtDiskReadWithSync(
IN PVOID Context
)
/*++
Routine Description:
This routine reads the sector at device level to get around a PCI
bug.
Arguments:
Context - A pointer to the device extension.
Return Value:
Always FALSE.
--*/
{
PDISK_EXTENSION diskExtension = ((PBAD_PCI_BLOCK)Context)->DiskExtension;
PVOID BufferToReadTo = ((PBAD_PCI_BLOCK)Context)->Buffer;
PCONTROLLER_EXTENSION controllerExtension =
diskExtension->ControllerExtension;
READ_CONTROLLER_BUFFER(
controllerExtension->ControllerAddress + DATA_REGISTER,
BufferToReadTo,
diskExtension->BytesPerInterrupt
);
return FALSE;
}
BOOLEAN
AtDiskWriteWithSync(
IN PVOID Context
)
/*++
Routine Description:
This routine writes the sector at device level to get around a PCI
bug.
Arguments:
Context - A pointer to the device extension.
Return Value:
Always FALSE.
--*/
{
PDISK_EXTENSION diskExtension = ((PBAD_PCI_BLOCK)Context)->DiskExtension;
PVOID BufferToWriteFrom = ((PBAD_PCI_BLOCK)Context)->Buffer;
PCONTROLLER_EXTENSION controllerExtension =
diskExtension->ControllerExtension;
WRITE_CONTROLLER_BUFFER(
controllerExtension->ControllerAddress + DATA_REGISTER,
BufferToWriteFrom,
diskExtension->BytesPerInterrupt
);
return FALSE;
}
BOOLEAN
AtDiskStartReset(
IN PVOID Context
)
/*++
Routine Description:
This routine is used to start off the reset cycle for the controller.
It is called at DIRQL.
Arguments:
Context - A pointer to the device extension for the "failing" drive.
Return Value:
Always FALSE.
--*/
{
PDISK_EXTENSION diskExtension = Context;
PCONTROLLER_EXTENSION controllerExtension =
diskExtension->ControllerExtension;
AtDump(
ATERRORS,
("ATDISK: Starting reset with failing extension: %x\n",diskExtension));
//
// Let it Be Known that we are resetting the controller.
//
controllerExtension->ResettingController = RESET_FIRST_DRIVE_SET;
//
// Reset the controller, since its state isn't what we expect.
// Wait up to 2 seconds between writes to the control port.
// Normally we don't like to wait at DIRQL, but this won't generate
// an interrupt and 10us isn't so bad.
//
WRITE_CONTROLLER(
controllerExtension->ControlPortAddress,
RESET_CONTROLLER );
AtWaitControllerBusy(
controllerExtension->ControllerAddress + STATUS_REGISTER,
20,
75000
);
WRITE_CONTROLLER(
controllerExtension->ControlPortAddress,
( ENABLE_INTERRUPTS | controllerExtension->ControlFlags ) );
//
// Every controller reset must be followed by setting the drive
// parameters of all attached drives. This DOES cause an
// interrupt.
//
controllerExtension->InterruptRequiresDpc = TRUE;
controllerExtension->InterruptTimer = START_TIMER;
controllerExtension->WhichDeviceObject = diskExtension->DeviceObject;
//
// Before we write to the controller, wait until we're sure it's
// not still busy setting itself up. We don't like waiting here,
// but there's no interrupt to wait on, the wait should be very
// short, and this code is only executed if the hardware gets
// messed up. If it's not ready after 3 seconds, just blast ahead...
// the operation won't work, but it will time out and be handled
// later. Note that we ordinarily don't want to wait this long
// in a driver, but we don't wait longer than we have to and we
// may just have to. This isn't an ordinary event.
//
( VOID ) AtWaitControllerReady( controllerExtension, 20, 150000 );
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + DRIVE_HEAD_REGISTER,
( diskExtension->DeviceUnit |
( diskExtension->TracksPerCylinder - 1 ) ) );
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + SECTOR_COUNT_REGISTER,
diskExtension->SectorsPerTrack );
WRITE_CONTROLLER(
controllerExtension->ControllerAddress + COMMAND_REGISTER,
SET_DRIVE_PARAMETERS_COMMAND );
//
// When the interrupt comes, the DPC will see ResettingController
// and take care of setting parameters on the second drive and
// restarting any packets that were in progress.
//
return FALSE;
}
VOID
AtDiskCheckTimer(
IN PDEVICE_OBJECT DeviceObject,
IN OUT PVOID Context
)
/*++
Routine Description:
This routine is called at DISPATCH_LEVEL once every second by the
I/O system.
If the timer is "set" (greater than 0) this routine will KeSync a
routine to decrement it. If it ever reaches 0, the hardware is
assumed to be in an unknown state, and so we log an error and
initiate a reset.
If a timeout occurs while resetting the controller, the KeSync'd
routine will return an error, and this routine will fail any IRPs
currently being processed. Future IRPs will try the hardware again.
When this routine is called, the driver state is impossible to
predict. However, when it is called and the timer is running, we
know that one of the disks on the controller is expecting an
interrupt. So no new packets are starting on the current disk due
to device queues, and no code should be processing this packet since
the packet is waiting for an interrupt.
Arguments:
DeviceObject - a pointer to the device object associated with this
timer (always the first disk on the controller).
ControllerExtension - a pointer to the controller extension data.
Return Value:
None.
--*/
{
PCONTROLLER_EXTENSION controllerExtension;
PDISK_EXTENSION diskExtension;
UNREFERENCED_PARAMETER( DeviceObject );
controllerExtension = Context;
//
// If the BusyOnStartDevice is true, then we KNOW that the counter below
// is minus 1. We trust that hardware will reset the busy bit at some
// time in the near future. We check to see if the pointer is non-NULL.
// If it is !NULL then the ONLY way it will get set back to NULL is
// by THIS routine calling AtStartDevice and having start device clear
// the pointer and "restart" the IO. We don't need to proceed with this
// timer routine if the pointer was !NULL because nothing could have
// been happening.
//
if (controllerExtension->BusyDevice) {
KeSynchronizeExecution(
controllerExtension->InterruptObject,
AtStartDevice,
controllerExtension->BusyDevice );
return;
}
//
// When the counter is -1, the timer is "off" so we don't want to do
// anything. If it's on, we'll have to synchronize execution with
// other routines while we mess with the variables (and, potentially,
// the hardware).
//
if ( controllerExtension->InterruptTimer == CANCEL_TIMER ) {
return;
}
//
// In the unlikely event that we attempt to reset the controller due
// to a timeout AND that reset times out, we will need to fail the
// IRP that was in progress at the first timeout occurred.
// WhichDeviceObject might be different when the second timeout occurs,
// so we save it here. Note that this routine can't, in general, alter
// controller extension variables; however, FirstFailingDeviceObject
// is safe since this is the only routine that touches it.
//
if ( controllerExtension->ResettingController == RESET_NOT_RESETTING ) {
controllerExtension->FirstFailingDeviceObject =
controllerExtension->WhichDeviceObject;
}
if ( !(KeSynchronizeExecution(
controllerExtension->InterruptObject,
AtCheckTimerSync,
controllerExtension ) ) ) {
UCHAR MajorFunctionCode = 0;
diskExtension =
controllerExtension->FirstFailingDeviceObject->DeviceExtension;
if ( controllerExtension->FirstFailingDeviceObject->CurrentIrp ) {
MajorFunctionCode =
IoGetCurrentIrpStackLocation(
controllerExtension->FirstFailingDeviceObject->CurrentIrp
)->MajorFunction;
}
//
// AtCheckTimerSync() only returns false if we get a timeout
// while resetting the controller. This will probably never
// happen. But just in case, fail the current IRP. Future
// IRPs will probably do the same thing, unless the hardware
// suddenly straightens itself out.
//
AtDump(
ATERRORS,
( "AtDisk ERROR: time-out during reset, failing IRP\n" ));
AtLogError(
controllerExtension->FirstFailingDeviceObject,
diskExtension->SequenceNumber,
MajorFunctionCode,
diskExtension->IrpRetryCount,
6,
STATUS_DISK_RESET_FAILED,
IO_ERR_TIMEOUT,
ERROR_LOG_TYPE_TIMEOUT_DURING_RESET,
0,
0,
0,
0,
0,
0,
0);
//
// We're done with the reset. Return the IRP that was being
// processed with an error, and release the controller object.
//
if ( controllerExtension->FirstFailingDeviceObject->CurrentIrp !=
NULL ) {
AtFinishPacket( diskExtension, STATUS_DISK_RESET_FAILED );
}
controllerExtension->ResettingController = RESET_NOT_RESETTING;
IoFreeController( controllerExtension->ControllerObject );
}
}
BOOLEAN
AtCheckTimerSync(
IN OUT PVOID Context
)
/*++
Routine Description:
This routine is called at DIRQL by AtDiskCheckTimer() when
InterruptTimer is greater than 0.
If the timer is "set" (greater than 0) this routine will decrement
it. If it ever reaches 0, the hardware is assumed to be in an
unknown state, and so we log an error and initiate a reset.
When this routine is called, the driver state is impossible to
predict. However, when it is called and the timer is running, we
know that one of the disks on the controller is expecting an
interrupt. So, no new packets are starting on the current disk due
to device queues, and no code should be processing this packet since
the packet is waiting for an interrupt. The controller object must
be held.
Arguments:
Context - a pointer to the controller extension.
Return Value:
Generally TRUE.
FALSE is only returned if the controller timed out while resetting
the drive, so this means that the hardware state is unknown.
--*/
{
PCONTROLLER_EXTENSION controllerExtension;
PDISK_EXTENSION diskExtension;
controllerExtension = Context;
//
// When the counter is -1, the timer is "off" so we don't want to do
// anything. It may have changed since we last checked it in
// AtDiskCheckTimer().
//
if ( controllerExtension->InterruptTimer == CANCEL_TIMER ) {
return TRUE;
}
//
// The timer is "on", so decrement it.
//
controllerExtension->InterruptTimer--;
//
// If we hit zero, the timer has expired and we'll reset the
// controller.
//
if ( controllerExtension->InterruptTimer == EXPIRED_TIMER ) {
//
// Make sure that we're working with the device extension of the
// drive that timed out. The device extension of Disk1 was passed
// in.
//
diskExtension = controllerExtension->WhichDeviceObject->DeviceExtension;
//
// If we were ALREADY resetting the controller when it timed out,
// there's something seriously wrong.
//
if ( controllerExtension->ResettingController != RESET_NOT_RESETTING ) {
//
// Returning FALSE will cause the current IRP to be completed
// with an error. Future IRPs will probably get a timeout and
// attempt to reset the controller again. This will probably
// never happen.
//
controllerExtension->InterruptTimer = CANCEL_TIMER;
return FALSE;
}
AtDiskStartReset(diskExtension);
} // timer expired
return TRUE;
}
NTSTATUS
AtWaitControllerReady(
PCONTROLLER_EXTENSION ControllerExtension,
ULONG MicrosecondsToDelay,
ULONG TimesToDelay
)
/*++
Routine Description:
This routine waits for the hard disk controller to turn off the BUSY
bit in the status register.
It waits any number of times (TimesToDelay) for any number of
microseconds (MicroSecondsToDelay). In between waits, it checks to
see if the busy bit has been turned off. If so, it returns early.
Arguments:
ControllerExtension - a pointer to the data area for the hard disk
controller in question.
MicrosecondsToDelay - the number of microseconds to delay each time
TimesToDelay - the number of times to delay
Return Value:
STATUS_SUCCESS if the BUSY bit was turned off; STATUS_TIMEOUT if the
BUSY bit was still on when we finished waiting.
--*/
{
ULONG loopCount = 0;
while ( ( READ_CONTROLLER(
ControllerExtension->ControllerAddress + STATUS_REGISTER ) &
BUSY_STATUS ) &&
( loopCount++ < TimesToDelay ) ) {
KeStallExecutionProcessor( MicrosecondsToDelay );
}
if ( loopCount == TimesToDelay ) {
AtDump(
ATERRORS,
( "Atdisk ERROR: controller not ready\n" ));
return STATUS_IO_TIMEOUT;
} else {
return STATUS_SUCCESS;
}
}
NTSTATUS
AtWaitControllerBusy(
PUCHAR StatusRegisterAddress,
ULONG MicrosecondsToDelay,
ULONG TimesToDelay
)
/*++
Routine Description:
This routine waits for the hard disk controller to turn ON the BUSY
bit in the status register.
It waits any number of times (TimesToDelay) for any number of
microseconds (MicroSecondsToDelay). In between waits, it checks to
see if the busy bit has been turned on. If so, it returns early.
Arguments:
StatusRegisterAddress - Where to check for busy.
MicrosecondsToDelay - the number of microseconds to delay each time
TimesToDelay - the number of times to delay
Return Value:
STATUS_SUCCESS if the BUSY bit was turned on; STATUS_TIMEOUT if the
BUSY bit was still off when we finished waiting.
--*/
{
ULONG loopCount = 0;
while ( !( READ_CONTROLLER(StatusRegisterAddress) & BUSY_STATUS ) &&
( loopCount++ < TimesToDelay ) ) {
KeStallExecutionProcessor( MicrosecondsToDelay );
}
if ( loopCount == TimesToDelay ) {
AtDump(
ATERRORS,
( "Atdisk ERROR: controller not busy\n" ));
return STATUS_IO_TIMEOUT;
} else {
return STATUS_SUCCESS;
}
}
VOID
AtLogError(
IN PDEVICE_OBJECT DeviceObject,
IN ULONG SequenceNumber,
IN UCHAR MajorFunctionCode,
IN UCHAR RetryCount,
IN ULONG UniqueErrorValue,
IN NTSTATUS FinalStatus,
IN NTSTATUS SpecificIOStatus,
IN CCHAR ErrorType,
IN UCHAR Parameter1,
IN UCHAR Parameter2,
IN UCHAR Parameter3,
IN UCHAR Parameter4,
IN UCHAR Parameter5,
IN UCHAR Parameter6,
IN UCHAR Parameter7
)
/*++
Routine Description:
This routine is called at DISPATCH_LEVEL by either
AtDiskDeferredProcedure() or AtDiskCheckTimer() if they
encounter an error.
This routine allocates an error log entry, copies the supplied text
to it, and requests that it be written to the error log file.
Arguments:
DeviceObject - a pointer to the device object associated with the
device that had the error.
SequenceNumber - A ulong value that is unique to an IRP over the
life of the irp in this driver - 0 generally means an error not
associated with an irp.
MajorFunctionCode - If there is an error associated with the irp,
this is the major function code of that irp.
RetryCount - The number of times a particular operation has been
retried.
UniqueErrorValue - A unique long word that identifies the particular
call to this function.
FinalStatus - The final status given to the irp that was associated
with this error. If this log entry is being made during one of
the retries this value will be STATUS_SUCCESS.
SpecificIOStatus - The IO status for a particular error.
ErrorType - the type of error being logged, defined by this driver.
Most types do not use the following parameters.
Parameter1 - for ERROR_LOG_TYPE_ERROR, this holds ERROR_REGISTER.
Parameter2 - for ERROR_LOG_TYPE_ERROR, this holds DRIVE_HEAD_REGISTER.
Parameter3 - for ERROR_LOG_TYPE_ERROR, this holds CYLINDER_HIGH_REGISTER.
Parameter4 - for ERROR_LOG_TYPE_ERROR, this holds CYLINDER_LOW_REGISTER.
Parameter5 - for ERROR_LOG_TYPE_ERROR, this holds SECTOR_NUMBER_REGISTER.
Parameter6 - for ERROR_LOG_TYPE_ERROR, this holds SECTOR_COUNT_REGISTER
Parameter7 - for ERROR_LOG_TYPE_ERROR, this holds STATUS_REGISTER.
Return Value:
None.
--*/
{
PIO_ERROR_LOG_PACKET errorLogEntry;
UCHAR extraAllocSize = 0;
PDISK_EXTENSION diskExtension = DeviceObject->DeviceExtension;
if (ErrorType == ERROR_LOG_TYPE_ERROR) {
//
// Allocate space for the seven parameters that came in as well
// as the 5 parameters that were used to start out this transfer
// and 4 bytes so that we can look at the geometry.
//
extraAllocSize = 16;
}
errorLogEntry = IoAllocateErrorLogEntry(
DeviceObject,
(UCHAR)(sizeof(IO_ERROR_LOG_PACKET) + extraAllocSize) );
if ( errorLogEntry != NULL ) {
errorLogEntry->ErrorCode = SpecificIOStatus;
errorLogEntry->SequenceNumber = SequenceNumber;
errorLogEntry->MajorFunctionCode = MajorFunctionCode;
errorLogEntry->RetryCount = RetryCount;
errorLogEntry->UniqueErrorValue = UniqueErrorValue;
errorLogEntry->FinalStatus = FinalStatus;
if (ErrorType == ERROR_LOG_TYPE_ERROR) {
PUCHAR errorValues = (PVOID)&errorLogEntry->DumpData[0];
errorLogEntry->DumpDataSize = 16;
//
// Give the geometry of the disk at the beginning of the dump data.
//
errorValues[0] = (UCHAR)diskExtension->SectorsPerTrack;
errorValues[1] = (UCHAR)diskExtension->TracksPerCylinder;
errorValues[2] = (UCHAR)diskExtension->NumberOfCylinders;
errorValues[3] = (UCHAR)(diskExtension->NumberOfCylinders >> 8);
//
// Show the CHS that we started with in the log as one long word at
// the beginning.
//
//
// Sector on track
//
errorValues[4] = (UCHAR)((diskExtension->FirstSectorOfTransfer %
diskExtension->SectorsPerTrack) + 1);
//
// Drive unit & head - for drive unit low bit of high nibble
// 0 is master, 1 is slave.
//
errorValues[5] = (UCHAR)(((diskExtension->FirstSectorOfTransfer /
diskExtension->SectorsPerTrack ) %
diskExtension->TracksPerCylinder) |
diskExtension->DeviceUnit);
//
// Low byte of cylinder
//
errorValues[6] = (UCHAR)((diskExtension->FirstSectorOfTransfer /
(diskExtension->SectorsPerTrack *
diskExtension->TracksPerCylinder)) & 0xff);
//
// High byte of cylinder.
//
errorValues[7] = (UCHAR)((diskExtension->FirstSectorOfTransfer /
(diskExtension->SectorsPerTrack *
diskExtension->TracksPerCylinder)) >> 8);
//
// Total of sectors in transfer.
//
errorValues[8] = (UCHAR)(diskExtension->TotalTransferLength /
diskExtension->BytesPerSector);
//
// Sector Count
//
errorValues[9] = Parameter6;
//
// Status register
//
errorValues[10] = Parameter7;
//
// Error register.
//
errorValues[11] = Parameter1;
//
// sector on error
//
errorValues[12] = Parameter5;
//
// Drive unit & head - for drive unit low bit of high nibble
// 0 is master, 1 is slave.
//
errorValues[13] = Parameter2;
//
// low byte of cylinder in error.
//
errorValues[14] = Parameter4;
//
// High byte of cylinder.
//
errorValues[15] = Parameter3;
} else {
errorLogEntry->DumpDataSize = 0;
}
IoWriteErrorLogEntry(errorLogEntry);
}
}
PVOID
AtGetTranslatedMemory(
IN INTERFACE_TYPE BusType,
IN ULONG BusNumber,
PHYSICAL_ADDRESS IoAddress,
ULONG NumberOfBytes,
BOOLEAN InIoSpace,
PBOOLEAN MappedAddress
)
/*++
Routine Description:
This routine maps an IO address to system address space.
Arguments:
BusType - what type of bus - eisa, mca, isa
IoBusNumber - which IO bus (for machines with multiple buses).
IoAddress - base device address to be mapped.
NumberOfBytes - number of bytes for which address is valid.
InIoSpace - indicates an IO address.
MappedAddress - indicates whether the address was mapped.
This only has meaning if the address returned
is non-null.
Return Value:
Mapped address
--*/
{
PHYSICAL_ADDRESS cardAddress;
ULONG addressSpace = InIoSpace;
PVOID Address;
if (!HalTranslateBusAddress(
BusType,
BusNumber,
IoAddress,
&addressSpace,
&cardAddress
)) {
*MappedAddress = FALSE;
return NULL;
}
//
// Map the device base address into the virtual address space
// if the address is in memory space.
//
if (!addressSpace) {
Address = MmMapIoSpace(
cardAddress,
NumberOfBytes,
FALSE
);
*MappedAddress = (BOOLEAN)((Address)?(TRUE):(FALSE));
} else {
*MappedAddress = FALSE;
Address = (PVOID)cardAddress.LowPart;
}
return Address;
}
BOOLEAN
AtReportUsage(
IN PCONFIG_DATA ConfigData,
IN UCHAR ControllerNumber,
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
This routine will build up a resource list using the
data for this particular controller as well as all
previous *successfully* configured controllers.
N.B. This routine assumes that it called in controller
number order.
Arguments:
ConfigData - a pointer to the structure that describes the
controller and the disks attached to it, as given to us by the
configuration manager.
ControllerNumber - which controller in ConfigData we are
about to try to report.
DriverObject - a pointer to the object that represents this device
driver.
Return Value:
TRUE if no conflict was detected, FALSE otherwise.
--*/
{
ULONG sizeOfResourceList;
ULONG numberOfFrds;
ULONG i;
PCM_RESOURCE_LIST resourceList;
PCM_FULL_RESOURCE_DESCRIPTOR nextFrd;
//
// Loop through all of the controllers previous to this
// controller. If the controllers previous to this one
// didn't have a conflict, then accumulate the size of the
// CM_FULL_RESOURCE_DESCRIPTOR associated with it.
//
for (
i = 0,numberOfFrds = 0,sizeOfResourceList = 0;
i <= (ULONG)ControllerNumber;
i++
) {
if (ConfigData->Controller[i].OkToUseThisController) {
sizeOfResourceList += sizeof(CM_FULL_RESOURCE_DESCRIPTOR);
//
// The full resource descriptor already contains one
// partial. Make room for two more.
//
// It will hold the irq "prd", the controller "csr" "prd", and
// the controller port "prd".
//
sizeOfResourceList += 2*sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
numberOfFrds++;
}
}
//
// Now we increment the length of the resource list by field offset
// of the first frd. This will give us the length of what preceeds
// the first frd in the resource list.
//
sizeOfResourceList += FIELD_OFFSET(
CM_RESOURCE_LIST,
List[0]
);
resourceList = ExAllocatePool(
PagedPool,
sizeOfResourceList
);
if (!resourceList) {
return FALSE;
}
//
// Zero out the field
//
RtlZeroMemory(
resourceList,
sizeOfResourceList
);
resourceList->Count = numberOfFrds;
nextFrd = &resourceList->List[0];
for (
i = 0;
numberOfFrds;
i++
) {
if (ConfigData->Controller[i].OkToUseThisController) {
PCM_PARTIAL_RESOURCE_DESCRIPTOR partial;
nextFrd->InterfaceType = ConfigData->Controller[i].InterfaceType;
nextFrd->BusNumber = ConfigData->Controller[i].BusNumber;
//
// We are only going to report 3 items no matter what
// was in the original.
//
nextFrd->PartialResourceList.Count = 3;
//
// Now fill in the two port data. We don't wish to share
// this port range with anyone
//
partial = &nextFrd->PartialResourceList.PartialDescriptors[0];
partial->Type = CmResourceTypePort;
partial->ShareDisposition = CmResourceShareDriverExclusive;
partial->Flags = CM_RESOURCE_PORT_IO;
partial->u.Port.Start =
ConfigData->Controller[i].OriginalControllerBaseAddress;
partial->u.Port.Length =
ConfigData->Controller[i].RangeOfControllerBase;
partial++;
partial->Type = CmResourceTypePort;
partial->ShareDisposition = CmResourceShareDriverExclusive;
partial->Flags = CM_RESOURCE_PORT_IO;
partial->u.Port.Start =
ConfigData->Controller[i].OriginalControlPortAddress;
partial->u.Port.Length =
ConfigData->Controller[i].RangeOfControlPort;
partial++;
//
// Now fill in the irq stuff.
//
partial->Type = CmResourceTypeInterrupt;
partial->u.Interrupt.Level =
ConfigData->Controller[i].OriginalControllerIrql;
partial->u.Interrupt.Vector =
ConfigData->Controller[i].OriginalControllerVector;
if (nextFrd->InterfaceType == MicroChannel) {
partial->ShareDisposition = CmResourceShareShared;
} else {
partial->ShareDisposition = CmResourceShareDriverExclusive;
}
if (ConfigData->Controller[i].InterruptMode == Latched) {
partial->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
} else {
partial->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
}
partial++;
nextFrd = (PVOID)partial;
numberOfFrds--;
}
}
IoReportResourceUsage(
NULL,
DriverObject,
resourceList,
sizeOfResourceList,
NULL,
NULL,
0,
FALSE,
&ConfigData->Controller[ControllerNumber].OkToUseThisController
);
//
// The above routine sets the boolean the parameter
// to TRUE if a conflict was detected.
//
ConfigData->Controller[ControllerNumber].OkToUseThisController =
!ConfigData->Controller[ControllerNumber].OkToUseThisController;
ExFreePool(resourceList);
return ConfigData->Controller[ControllerNumber].OkToUseThisController;
}
BOOLEAN
GetGeometryFromIdentify(
PCONTROLLER_DATA ControllerData,
BOOLEAN Primary
)
/*++
Routine Description:
This updates geometry information in a disk extension
from a BIOS parameter table entry.
Arguments:
ControllerData - Description of the controller.
Primary - Whether this is the primary disk on the controller address.
Return Value:
Nothing.
--*/
{
IDENTIFY_DATA identifyBuffer;
PDRIVE_DATA driveData = (Primary)?(&ControllerData->Disk[0]):
(&ControllerData->Disk[1]);
//
// Issue IDENTIFY command.
//
if (!IssueIdentify(ControllerData,
(PUCHAR)&identifyBuffer,
Primary)) {
return FALSE;
}
//
// Initialize this drive.
//
driveData->BytesPerSector = 512;
driveData->BytesPerInterrupt = 512;
driveData->ReadCommand = 0x20;
driveData->WriteCommand = 0x30;
driveData->VerifyCommand = 0x40;
driveData->PretendNumberOfCylinders =
driveData->NumberOfCylinders =
(USHORT)identifyBuffer.NumberOfCylinders;
driveData->PretendTracksPerCylinder =
driveData->TracksPerCylinder =
identifyBuffer.NumberOfHeads;
driveData->PretendSectorsPerTrack =
driveData->SectorsPerTrack =
identifyBuffer.SectorsPerTrack;
//
// Since we don't know any better, and no drives within recent memory
// use write precomp, set it to MAXUSHORT so that we don't set it
// anymore.
//
driveData->WritePrecomp = MAXUSHORT;
return TRUE;
}
BOOLEAN
IssueIdentify(
PCONTROLLER_DATA ControllerData,
PUCHAR Buffer,
BOOLEAN Primary
)
/*++
Routine Description:
Issue 0xEC IDENTIFY command to collect disk information.
Arguments:
ControllerData - Description of controller.
Buffer - A place to store geometry.
Primary - Whether this is the primary disk on the controller address.
Return Value:
TRUE if IDENTIFY command successful.
--*/
{
ULONG i;
UCHAR statusByte;
//
// Select disk 0 or 1.
//
WRITE_CONTROLLER(ControllerData->ControllerBaseAddress + DRIVE_HEAD_REGISTER,
(Primary)?(DRIVE_1):(DRIVE_2));
//
// If the second drive is selected but it doesn't exist the controller
// may behave randomly. Check that the status register makes sense.
//
statusByte = READ_CONTROLLER(ControllerData->ControllerBaseAddress + STATUS_REGISTER);
//
// Get rid of the IDX bit.
//
statusByte &= 0xfc;
if (statusByte != 0x50) {
//
// Select drive zero again so that the controller
// will return to normal behaviour.
//
WRITE_CONTROLLER(ControllerData->ControllerBaseAddress + DRIVE_HEAD_REGISTER,
DRIVE_1);
return FALSE;
}
//
// Send IDENTIFY command.
//
WRITE_CONTROLLER(ControllerData->ControllerBaseAddress + COMMAND_REGISTER,
IDENTIFY_COMMAND);
//
// Wait for up to 3 seconds for DRQ or ERROR.
//
for (i=0; i<300000; i++) {
statusByte = READ_CONTROLLER(ControllerData->ControllerBaseAddress + STATUS_REGISTER);
if (statusByte & ERROR_STATUS) {
return FALSE;
} else if (statusByte & DATA_REQUEST_STATUS) {
break;
} else {
KeStallExecutionProcessor(10L);
}
}
if (i == 10000) {
return FALSE;
}
//
// Suck out 256 words.
//
READ_CONTROLLER_BUFFER(
ControllerData->ControllerBaseAddress + DATA_REGISTER,
Buffer,
512);
AtDump(
ATINIT,
("ATDISK: Identify Data -\n")
);
{
PIDENTIFY_DATA id = (PVOID)&Buffer[0];
if (id->GeneralConfiguration & 0x8000) {
AtDump(
ATINIT,
(" non-magnetic media\n")
);
}
if (id->GeneralConfiguration & 0x4000) {
AtDump(
ATINIT,
(" format speed tolerance gap required\n")
);
}
if (id->GeneralConfiguration & 0x2000) {
AtDump(
ATINIT,
(" track offset option available\n")
);
}
if (id->GeneralConfiguration & 0x1000) {
AtDump(
ATINIT,
(" data strobe offset option available\n")
);
}
if (id->GeneralConfiguration & 0x0800) {
AtDump(
ATINIT,
(" rotational speed tolerance is > 0,5%\n")
);
}
if (id->GeneralConfiguration & 0x0400) {
AtDump(
ATINIT,
(" disk transfer rate > 10Mbs\n")
);
}
if (id->GeneralConfiguration & 0x0200) {
AtDump(
ATINIT,
(" disk transfer rate > 5Mbs but <= 10Mbs\n")
);
}
if (id->GeneralConfiguration & 0x0100) {
AtDump(
ATINIT,
(" disk transfer rate <= 5Mbs\n")
);
}
if (id->GeneralConfiguration & 0x0080) {
AtDump(
ATINIT,
(" removeable cartridge drive\n")
);
}
if (id->GeneralConfiguration & 0x0040) {
AtDump(
ATINIT,
(" fixed drive\n")
);
}
if (id->GeneralConfiguration & 0x0020) {
AtDump(
ATINIT,
(" spindle motor control option implemented\n")
);
}
if (id->GeneralConfiguration & 0x0010) {
AtDump(
ATINIT,
(" head switch time > 15us\n")
);
}
if (id->GeneralConfiguration & 0x0008) {
AtDump(
ATINIT,
(" not MFM encoded\n")
);
}
if (id->GeneralConfiguration & 0x0004) {
AtDump(
ATINIT,
(" soft sectored\n")
);
}
if (id->GeneralConfiguration & 0x0002) {
AtDump(
ATINIT,
(" hard sectored\n")
);
}
AtDump(
ATINIT,
(" Number of Cylinders: %d\n",
id->NumberOfCylinders)
);
AtDump(
ATINIT,
(" Number of heads: %d\n",
id->NumberOfHeads)
);
AtDump(
ATINIT,
(" Unformatted bytes per track: %d\n",
id->UnformattedBytesPerTrack)
);
AtDump(
ATINIT,
(" Unformatted bytes per sector: %d\n",
id->UnformattedBytesPerSector)
);
AtDump(
ATINIT,
(" Sectors per track: %d\n",
id->SectorsPerTrack)
);
{
PUSHORT tempS;
UCHAR tempByte;
ULONG k;
//
// Byte flip model number, revision, and serial number string.
//
tempS = id->ModelNumber;
for (k=0; k<20; k++) {
tempByte = (UCHAR)(tempS[k] & 0x00FF);
tempS[k] = tempS[k] >> 8;
tempS[k] |= tempByte << 8;
}
tempS = id->FirmwareRevision;
for (k=0; k<4; k++) {
tempByte = (UCHAR)(tempS[k] & 0x00FF);
tempS[k] = tempS[k] >> 8;
tempS[k] |= tempByte << 8;
}
tempS = id->SerialNumber;
for (k=0; k<10; k++) {
tempByte = (UCHAR)(tempS[k] & 0x00FF);
tempS[k] = tempS[k] >> 8;
tempS[k] |= tempByte << 8;
}
}
AtDump(
ATINIT,
(" Serial number: %.20s\n",
(PUCHAR)&id->SerialNumber[0])
);
if (id->BufferType == 0) {
AtDump(
ATINIT,
(" Buffer type unspecified\n")
);
}
if (id->BufferType == 1) {
AtDump(
ATINIT,
(" Buffer type single port - no simultanous transfer\n")
);
}
if (id->BufferType == 2) {
AtDump(
ATINIT,
(" Buffer type dual port - simultanous transfer capable\n")
);
}
if (id->BufferType == 3) {
AtDump(
ATINIT,
(" Buffer type dual port - simultanous transfer capable - read cache\n")
);
}
if (id->BufferType >= 4) {
AtDump(
ATINIT,
(" Buffer type reserved\n")
);
}
if (id->BufferSectorSize == 0) {
AtDump(
ATINIT,
(" Unspecified buffer size\n")
);
} else {
AtDump(
ATINIT,
(" Buffer size in sectors: %d\n",
id->BufferSectorSize)
);
}
if (id->NumberOfEccBytes == 0) {
AtDump(
ATINIT,
(" Number of Ecc bytes is unspecified\n")
);
} else {
AtDump(
ATINIT,
(" Number of Ecc bytes or r/w long: %d\n",
id->NumberOfEccBytes)
);
}
AtDump(
ATINIT,
(" Firmware revision: %.8s\n",
(PUCHAR)&id->FirmwareRevision[0])
);
AtDump(
ATINIT,
(" Model number: %.40s\n",
(PUCHAR)&id->ModelNumber[0])
);
if (id->MaximumBlockTransfer == 0) {
AtDump(
ATINIT,
(" Read/Write multiple not implmeneted\n")
);
} else {
AtDump(
ATINIT,
(" Maximum sectors/interrupt on read/write multiple: %d\n",
id->MaximumBlockTransfer)
);
}
if (id->DoubleWordIo == 0) {
AtDump(
ATINIT,
(" Can not perform double word IO\n")
);
} else if (id->DoubleWordIo == 1) {
AtDump(
ATINIT,
(" Can perform double word IO\n")
);
} else {
AtDump(
ATINIT,
(" Unknown doubleword specifier\n")
);
}
if (id->Capabilities & 0x0200) {
AtDump(
ATINIT,
(" LBA mode supported\n")
);
} else {
AtDump(
ATINIT,
(" LBA mode NOT supported\n")
);
}
if (id->Capabilities & 0x0100) {
AtDump(
ATINIT,
(" DMA supported\n")
);
} else {
AtDump(
ATINIT,
(" DMA NOT supported\n")
);
}
AtDump(
ATINIT,
(" PIO cycle timing mode: %x\n",
id->PioCycleTimingMode)
);
AtDump(
ATINIT,
(" DMA cycle timing mode: %x\n",
id->DmaCycleTimingMode)
);
if ((id->TranslationFieldsValid & 1) == 0) {
AtDump(
ATINIT,
(" Current size fields MAY be valid\n")
);
} else {
AtDump(
ATINIT,
(" Current size fields ARE valid\n")
);
}
AtDump(
ATINIT,
(" Current number of cylinders: %d\n",
id->NumberOfCurrentCylinders)
);
AtDump(
ATINIT,
(" Current number of heads: %d\n",
id->NumberOfCurrentHeads)
);
AtDump(
ATINIT,
(" Current number of sectors/track: %d\n",
id->CurrentSectorsPerTrack)
);
AtDump(
ATINIT,
(" Current sector capacity: %d\n",
id->CurrentSectorCapacity)
);
AtDump(
ATINIT,
(" Sectors per interrupt with r/w multiple: %d\n",
id->MultiSectorCount)
);
if (id->MultiSectorSettingValid & 1) {
AtDump(
ATINIT,
(" Multi sector setting valid\n")
);
} else {
AtDump(
ATINIT,
(" Multi sector setting is INVALID\n")
);
}
AtDump(
ATINIT,
(" Total user addressable sectors: %d\n",
id->TotalUserAddressableSectors)
);
AtDump(
ATINIT,
(" Single word dma modes supported: %x\n",
id->SingleDmaModesSupported)
);
AtDump(
ATINIT,
(" Single word trasfer mode active: %x\n",
id->SingleDmaTransferActive)
);
AtDump(
ATINIT,
(" Multi word dma modes supported: %x\n",
id->MultiDmaModesSupported)
);
AtDump(
ATINIT,
(" Multi word trasfer mode active: %x\n",
id->MultiDmaTransferActive)
);
}
return TRUE;
}
VOID
AtBuildDeviceMap(
IN ULONG ControllerNumber,
IN ULONG DiskNumber,
IN PHYSICAL_ADDRESS ControllerAddress,
IN KIRQL Irql,
IN PDRIVE_DATA Disk,
IN PIDENTIFY_DATA DiskData,
IN BOOLEAN PCCard
)
/*++
Routine Description:
The routine puts vendor and model information from the IDENTIFY
command into the device map in the registry.
Arguments:
ControllerNumber - Supplies the current controller number.
DiskNumber - Supplies the current disk on the controller.
ControllerAddress - The untranslated address of the disk controller.
Irql - The untranslated interrupt of the controller.
Disk - Address of the structure that holds our determined geometry
data.
DiskData - Address of disk IDENTIFY data.
Return Value:
None.
--*/
{
UNICODE_STRING name;
UNICODE_STRING unicodeString;
ANSI_STRING ansiString;
HANDLE key;
HANDLE controllerKey;
HANDLE diskKey;
OBJECT_ATTRIBUTES objectAttributes;
NTSTATUS status;
ULONG disposition;
ULONG tempLong;
PCHAR junkPtr;
RtlInitUnicodeString(
&name,
L"\\Registry\\Machine\\Hardware\\DeviceMap\\AtDisk"
);
//
// Initialize the object for the key.
//
InitializeObjectAttributes( &objectAttributes,
&name,
OBJ_CASE_INSENSITIVE,
NULL,
(PSECURITY_DESCRIPTOR) NULL );
//
// Create the key or open it.
//
status = ZwCreateKey(&key,
KEY_READ | KEY_WRITE,
&objectAttributes,
0,
(PUNICODE_STRING) NULL,
REG_OPTION_VOLATILE,
&disposition );
if (!NT_SUCCESS(status)) {
return;
}
status = AtCreateNumericKey(key, ControllerNumber, L"Controller ", &controllerKey);
ZwClose(key);
if (!NT_SUCCESS(status)) {
return;
}
RtlInitUnicodeString(&name, L"Controller Address");
status = ZwSetValueKey(
controllerKey,
&name,
0,
REG_DWORD,
&ControllerAddress.LowPart,
sizeof(ControllerAddress.LowPart));
RtlInitUnicodeString(&name, L"Controller Interrupt");
tempLong = (ULONG)Irql;
status = ZwSetValueKey(
controllerKey,
&name,
0,
REG_DWORD,
&tempLong,
sizeof(ULONG));
//
// Indicate if the controller is a PCCARD
//
if (PCCard) {
RtlInitUnicodeString(&name, L"PCCARD");
tempLong = 1;
status = ZwSetValueKey(
controllerKey,
&name,
0,
REG_DWORD,
&tempLong,
sizeof(ULONG));
}
//
// Create a key entry for the disk.
//
status = AtCreateNumericKey(controllerKey, DiskNumber, L"Disk ", &diskKey);
if (!NT_SUCCESS(status)) {
ZwClose(controllerKey);
return;
}
//
// Get the Identifier from the identify data.
//
RtlInitUnicodeString(&name, L"Identifier");
ansiString.MaximumLength = 40;
ansiString.Buffer = (PUCHAR)DiskData->ModelNumber;
junkPtr = memchr(
&ansiString.Buffer[0],
0x00,
ansiString.MaximumLength
);
if (!junkPtr) {
ansiString.Length = ansiString.MaximumLength;
} else {
ansiString.Length = junkPtr - &ansiString.Buffer[0];
}
status = RtlAnsiStringToUnicodeString(
&unicodeString,
&ansiString,
TRUE
);
if (NT_SUCCESS(status)) {
status = ZwSetValueKey(
diskKey,
&name,
0,
REG_SZ,
unicodeString.Buffer,
unicodeString.Length + sizeof(wchar_t));
RtlFreeUnicodeString(&unicodeString);
}
//
// Write the firmware revision to the registry.
//
RtlInitUnicodeString(&name, L"Firmware revision");
ansiString.MaximumLength = 8;
ansiString.Buffer = (PUCHAR)DiskData->FirmwareRevision;
junkPtr = memchr(
&ansiString.Buffer[0],
0x00,
ansiString.MaximumLength
);
if (!junkPtr) {
ansiString.Length = ansiString.MaximumLength;
} else {
ansiString.Length = junkPtr - &ansiString.Buffer[0];
}
status = RtlAnsiStringToUnicodeString(
&unicodeString,
&ansiString,
TRUE
);
if (NT_SUCCESS(status)) {
status = ZwSetValueKey(
diskKey,
&name,
0,
REG_SZ,
unicodeString.Buffer,
unicodeString.Length + sizeof(wchar_t));
RtlFreeUnicodeString(&unicodeString);
}
//
// Write the serial number to the registry.
//
RtlInitUnicodeString(&name, L"Serial number");
ansiString.MaximumLength = 20;
ansiString.Buffer = (PUCHAR)DiskData->SerialNumber;
junkPtr = memchr(
&ansiString.Buffer[0],
0x00,
ansiString.MaximumLength
);
if (!junkPtr) {
ansiString.Length = ansiString.MaximumLength;
} else {
ansiString.Length = junkPtr - &ansiString.Buffer[0];
}
status = RtlAnsiStringToUnicodeString(
&unicodeString,
&ansiString,
TRUE
);
if (NT_SUCCESS(status)) {
status = ZwSetValueKey(
diskKey,
&name,
0,
REG_SZ,
unicodeString.Buffer,
unicodeString.Length + sizeof(wchar_t));
RtlFreeUnicodeString(&unicodeString);
}
//
// Writ the data that the identify command found.
//
//
// Write number of cylinders to registry.
//
RtlInitUnicodeString(&name, L"Identify - Number of cylinders");
tempLong = (ULONG)DiskData->NumberOfCylinders;
status = ZwSetValueKey(
diskKey,
&name,
0,
REG_DWORD,
&tempLong,
sizeof(ULONG));
//
// Write number of heads to registry.
//
RtlInitUnicodeString(&name, L"Identify - Number of heads");
tempLong = DiskData->NumberOfHeads;
status = ZwSetValueKey(
diskKey,
&name,
0,
REG_DWORD,
&tempLong,
sizeof(ULONG));
//
// Write track size to registry.
//
RtlInitUnicodeString(&name, L"Identify - Sectors per track");
tempLong = DiskData->SectorsPerTrack;
status = ZwSetValueKey(
diskKey,
&name,
0,
REG_DWORD,
&tempLong,
sizeof(ULONG));
//
// Write the apparent geometry data.
//
//
// Write number of cylinders to registry.
//
RtlInitUnicodeString(&name, L"Apparent - Number of cylinders");
tempLong = (ULONG)Disk->PretendNumberOfCylinders;
status = ZwSetValueKey(
diskKey,
&name,
0,
REG_DWORD,
&tempLong,
sizeof(ULONG));
//
// Write number of heads to registry.
//
RtlInitUnicodeString(&name, L"Apparent - Number of heads");
tempLong = Disk->PretendTracksPerCylinder;
status = ZwSetValueKey(
diskKey,
&name,
0,
REG_DWORD,
&tempLong,
sizeof(ULONG));
//
// Write track size to registry.
//
RtlInitUnicodeString(&name, L"Apparent - Sectors per track");
tempLong = Disk->PretendSectorsPerTrack;
status = ZwSetValueKey(
diskKey,
&name,
0,
REG_DWORD,
&tempLong,
sizeof(ULONG));
//
// Write the actual geometry data.
//
//
// Write number of cylinders to registry.
//
RtlInitUnicodeString(&name, L"Actual - Number of cylinders");
tempLong = (ULONG)Disk->NumberOfCylinders;
status = ZwSetValueKey(
diskKey,
&name,
0,
REG_DWORD,
&tempLong,
sizeof(ULONG));
//
// Write number of heads to registry.
//
RtlInitUnicodeString(&name, L"Actual - Number of heads");
tempLong = Disk->TracksPerCylinder;
status = ZwSetValueKey(
diskKey,
&name,
0,
REG_DWORD,
&tempLong,
sizeof(ULONG));
//
// Write track size to registry.
//
RtlInitUnicodeString(&name, L"Actual - Sectors per track");
tempLong = Disk->SectorsPerTrack;
status = ZwSetValueKey(
diskKey,
&name,
0,
REG_DWORD,
&tempLong,
sizeof(ULONG));
ZwClose(diskKey);
ZwClose(controllerKey);
return;
}
VOID
AtReWriteDeviceMap(
IN ULONG ControllerNumber,
IN ULONG DiskNumber,
IN ULONG ApparentHeads,
IN ULONG ApparentCyl,
IN ULONG ApparentSec,
IN ULONG ActualHeads,
IN ULONG ActualCyl,
IN ULONG ActualSec
)
/*++
Routine Description:
The routine puts vendor and model information from the IDENTIFY
command into the device map in the registry.
Arguments:
ControllerNumber - Supplies the current controller number.
DiskNumber - Supplies the current disk on the controller.
Return Value:
None.
--*/
{
UNICODE_STRING name;
UNICODE_STRING unicodeString;
ANSI_STRING ansiString;
HANDLE key;
HANDLE controllerKey;
HANDLE diskKey;
OBJECT_ATTRIBUTES objectAttributes;
NTSTATUS status;
ULONG disposition;
ULONG tempLong;
PCHAR junkPtr;
RtlInitUnicodeString(
&name,
L"\\Registry\\Machine\\Hardware\\DeviceMap\\AtDisk"
);
//
// Initialize the object for the key.
//
InitializeObjectAttributes( &objectAttributes,
&name,
OBJ_CASE_INSENSITIVE,
NULL,
(PSECURITY_DESCRIPTOR) NULL );
//
// Create the key or open it.
//
status = ZwCreateKey(&key,
KEY_READ | KEY_WRITE,
&objectAttributes,
0,
(PUNICODE_STRING) NULL,
REG_OPTION_VOLATILE,
&disposition );
if (!NT_SUCCESS(status)) {
return;
}
status = AtCreateNumericKey(key, ControllerNumber, L"Controller ", &controllerKey);
ZwClose(key);
if (!NT_SUCCESS(status)) {
return;
}
//
// Create a key entry for the disk.
//
status = AtCreateNumericKey(controllerKey, DiskNumber, L"Disk ", &diskKey);
if (!NT_SUCCESS(status)) {
ZwClose(controllerKey);
return;
}
//
// Write the apparent geometry data.
//
//
// Write number of cylinders to registry.
//
RtlInitUnicodeString(&name, L"Apparent - Number of cylinders");
tempLong = ApparentCyl;
status = ZwSetValueKey(
diskKey,
&name,
0,
REG_DWORD,
&tempLong,
sizeof(ULONG));
//
// Write number of heads to registry.
//
RtlInitUnicodeString(&name, L"Apparent - Number of heads");
tempLong = ApparentHeads;
status = ZwSetValueKey(
diskKey,
&name,
0,
REG_DWORD,
&tempLong,
sizeof(ULONG));
//
// Write track size to registry.
//
RtlInitUnicodeString(&name, L"Apparent - Sectors per track");
tempLong = ApparentSec;
status = ZwSetValueKey(
diskKey,
&name,
0,
REG_DWORD,
&tempLong,
sizeof(ULONG));
//
// Write the actual geometry data.
//
//
// Write number of cylinders to registry.
//
RtlInitUnicodeString(&name, L"Actual - Number of cylinders");
tempLong = ActualCyl;
status = ZwSetValueKey(
diskKey,
&name,
0,
REG_DWORD,
&tempLong,
sizeof(ULONG));
//
// Write number of heads to registry.
//
RtlInitUnicodeString(&name, L"Actual - Number of heads");
tempLong = ActualHeads;
status = ZwSetValueKey(
diskKey,
&name,
0,
REG_DWORD,
&tempLong,
sizeof(ULONG));
//
// Write track size to registry.
//
RtlInitUnicodeString(&name, L"Actual - Sectors per track");
tempLong = ActualSec;
status = ZwSetValueKey(
diskKey,
&name,
0,
REG_DWORD,
&tempLong,
sizeof(ULONG));
ZwClose(diskKey);
ZwClose(controllerKey);
return;
}
VOID
AtMarkSkew(
IN ULONG ControllerNumber,
IN ULONG DiskNumber,
IN ULONG Skew
)
/*++
Routine Description:
This routine puts out the skew if factor for the drive.
Arguments:
ControllerNumber - Supplies the current controller number.
DiskNumber - Supplies the current disk on the controller.
Skew - The number of sectors skewed.
Return Value:
None.
--*/
{
UNICODE_STRING name;
UNICODE_STRING unicodeString;
ANSI_STRING ansiString;
HANDLE key;
HANDLE controllerKey;
HANDLE diskKey;
OBJECT_ATTRIBUTES objectAttributes;
NTSTATUS status;
ULONG disposition;
ULONG tempLong;
PCHAR junkPtr;
RtlInitUnicodeString(
&name,
L"\\Registry\\Machine\\Hardware\\DeviceMap\\AtDisk"
);
//
// Initialize the object for the key.
//
InitializeObjectAttributes( &objectAttributes,
&name,
OBJ_CASE_INSENSITIVE,
NULL,
(PSECURITY_DESCRIPTOR) NULL );
//
// Create the key or open it.
//
status = ZwCreateKey(&key,
KEY_READ | KEY_WRITE,
&objectAttributes,
0,
(PUNICODE_STRING) NULL,
REG_OPTION_VOLATILE,
&disposition );
if (!NT_SUCCESS(status)) {
return;
}
status = AtCreateNumericKey(key, ControllerNumber, L"Controller ", &controllerKey);
ZwClose(key);
if (!NT_SUCCESS(status)) {
return;
}
//
// Create a key entry for the disk.
//
status = AtCreateNumericKey(controllerKey, DiskNumber, L"Disk ", &diskKey);
if (!NT_SUCCESS(status)) {
ZwClose(controllerKey);
return;
}
RtlInitUnicodeString(&name, L"Hook");
status = ZwSetValueKey(
diskKey,
&name,
0,
REG_DWORD,
&Skew,
sizeof(Skew));
ZwClose(diskKey);
ZwClose(controllerKey);
return;
}
NTSTATUS
AtCreateNumericKey(
IN HANDLE Root,
IN ULONG Name,
IN PWSTR Prefix,
OUT PHANDLE NewKey
)
/*++
Routine Description:
This function creates a registry key. The name of the key is a string
version of numeric value passed in.
Arguments:
RootKey - Supplies a handle to the key where the new key should be inserted.
Name - Supplies the numeric value to name the key.
Prefix - Supplies a prefix name to add to name.
NewKey - Returns the handle for the new key.
Return Value:
Returns the status of the operation.
--*/
{
UNICODE_STRING string;
UNICODE_STRING stringNum;
OBJECT_ATTRIBUTES objectAttributes;
WCHAR bufferNum[16];
WCHAR buffer[64];
ULONG disposition;
NTSTATUS status;
//
// Copy the Prefix into a string.
//
string.Length = 0;
string.MaximumLength=64;
string.Buffer = buffer;
RtlInitUnicodeString(&stringNum, Prefix);
RtlCopyUnicodeString(&string, &stringNum);
//
// Create a port number key entry.
//
stringNum.Length = 0;
stringNum.MaximumLength = 16;
stringNum.Buffer = bufferNum;
status = RtlIntegerToUnicodeString(Name, 10, &stringNum);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Append the prefix and the numeric name.
//
RtlAppendUnicodeStringToString(&string, &stringNum);
InitializeObjectAttributes( &objectAttributes,
&string,
OBJ_CASE_INSENSITIVE,
Root,
(PSECURITY_DESCRIPTOR) NULL );
status = ZwCreateKey(NewKey,
KEY_READ | KEY_WRITE,
&objectAttributes,
0,
(PUNICODE_STRING) NULL,
REG_OPTION_VOLATILE,
&disposition );
return(status);
}
BOOLEAN
AtDiskControllerInfo(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath,
IN ULONG WhichController,
IN OUT PCONTROLLER_DATA Controller,
IN PHYSICAL_ADDRESS DefaultBaseAddress,
IN PHYSICAL_ADDRESS DefaultPortAddress,
IN KIRQL DefaultIrql,
IN INTERFACE_TYPE DefaultInterfaceType,
IN ULONG DefaultBusNumber,
IN BOOLEAN UseDefaults
)
/*++
Routine Description:
This will go out to the registry and see if a
specification exists for the particular controller.
Arguments:
DriverObject - Not used.
RegistryPath - Path to this drivers service node in
the current control set.
WhichController - Used to create the string ControllerX where
X is the value of WhichController.
Controller - Points to the controller config structure which
will get filled in if this routine sets up for a
controller.
DefaultBaseAddress - Holds the physical address that we should use
for the 7 regular registers if the UseDefaults
parameter is true.
DefaultPortAddress - Holds the physical address that we should use
for the drive control register if the UseDefaults
parameter is true.
DefaultIrql - Holds the "interrupt" that we should map if the UseDefaults
parameter is true.
DefaultInterfaceType - Holds the "bus type" that we should use if the
UseDefaults parameter is true.
DefaultBusNumber - Holds the bus number that we should use if the
UseDefaults parameter is true.
UseDefaults - If true, then map the default memory and vector even if
nothing could be found in the current control set.
ControllerBaseAddress - Holds the address of the data is received
and send.
ControllerPortAddress - Holds the address of the drive control
register.
Irql - The interrupt to be used for the controller.
Return Value:
FALSE if no data was found and UseDefaults was false.
--*/
{
//
// This will point to the structure that is used by RtlQueryRegistryValues
// to "direct" its search and retrieval of values.
//
PRTL_QUERY_REGISTRY_TABLE parameters = NULL;
UNICODE_STRING parametersPath;
UNICODE_STRING numberString;
BOOLEAN returnValue;
parametersPath.Buffer = NULL;
//
// Allocate the rtl query table.
//
parameters = ExAllocatePool(
PagedPool,
sizeof(RTL_QUERY_REGISTRY_TABLE)*6
);
if (!parameters) {
returnValue = FALSE;
goto FinishUp;
}
RtlZeroMemory(
parameters,
sizeof(RTL_QUERY_REGISTRY_TABLE)*6
);
//
// Form a path to our drivers Parameters subkey.
//
RtlInitUnicodeString(
&parametersPath,
NULL
);
//
// Allocate the path plus 100 WCHARS to express the number
// and 1 for the null pad.
//
parametersPath.MaximumLength = RegistryPath->Length +
2*sizeof(L"\\") +
sizeof(L"Parameters") +
101*sizeof(WCHAR);
parametersPath.Buffer = ExAllocatePool(
PagedPool,
parametersPath.MaximumLength
);
if (!parametersPath.Buffer) {
returnValue = FALSE;
goto FinishUp;
}
//
// Form the parameters path.
//
RtlZeroMemory(
parametersPath.Buffer,
parametersPath.MaximumLength
);
RtlAppendUnicodeStringToString(
&parametersPath,
RegistryPath
);
RtlAppendUnicodeToString(
&parametersPath,
L"\\Parameters\\"
);
//
// Now make a bogus unicode string that is the leftover
// of what we formed of the path.
//
numberString.MaximumLength = parametersPath.MaximumLength -
parametersPath.Length;
numberString.Length = 0;
numberString.Buffer = (PWSTR)(((PUCHAR)parametersPath.Buffer) +
parametersPath.Length);
if (!NT_SUCCESS(RtlIntegerToUnicodeString(
WhichController,
10,
&numberString
))) {
returnValue = FALSE;
goto FinishUp;
}
//
// Gather all of the "user specified" information from
// the registry.
//
parameters[0].Flags = RTL_QUERY_REGISTRY_REQUIRED |
RTL_QUERY_REGISTRY_DIRECT;
parameters[0].Name = L"BaseAddress";
parameters[0].EntryContext =
&Controller->OriginalControllerBaseAddress.LowPart;
parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT |
RTL_QUERY_REGISTRY_REQUIRED;
parameters[1].Name = L"Interrupt";
parameters[1].EntryContext = &Controller->OriginalControllerIrql;
parameters[2].Flags = RTL_QUERY_REGISTRY_DIRECT |
RTL_QUERY_REGISTRY_REQUIRED;
parameters[2].Name = L"DriveControl";
parameters[2].EntryContext =
&Controller->OriginalControlPortAddress.LowPart;
parameters[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
parameters[3].Name = L"BusNumber";
parameters[3].EntryContext = &Controller->BusNumber;
parameters[3].DefaultType = REG_DWORD;
parameters[3].DefaultData = &DefaultBusNumber;
parameters[3].DefaultLength = sizeof(ULONG);
parameters[4].Flags = RTL_QUERY_REGISTRY_DIRECT;
parameters[4].Name = L"InterfaceType";
parameters[4].EntryContext = &Controller->InterfaceType;
parameters[4].DefaultType = REG_DWORD;
parameters[4].DefaultData = &DefaultInterfaceType;
parameters[4].DefaultLength = sizeof(ULONG);
if (!NT_SUCCESS(RtlQueryRegistryValues(
RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
parametersPath.Buffer,
parameters,
NULL,
NULL
))) {
returnValue = FALSE;
} else {
returnValue = TRUE;
}
FinishUp: ;
//
// For some reason we couldn't get anything out of the registry
// for the additional controllers. If the caller specified use
// defaults then that means we should use the default values passed
// in and map the stuff anyway (This could happen on the standard
// atdisk controller addresses).
//
if (!returnValue && UseDefaults) {
Controller->InterfaceType = DefaultInterfaceType;
Controller->BusNumber = DefaultBusNumber;
Controller->OriginalControllerBaseAddress = DefaultBaseAddress;
Controller->OriginalControlPortAddress = DefaultPortAddress;
Controller->OriginalControllerIrql = DefaultIrql;
returnValue = TRUE;
}
if (returnValue) {
Controller->OriginalControllerVector =
Controller->OriginalControllerIrql;
if (Controller->InterfaceType == MicroChannel) {
Controller->SharableVector = TRUE;
Controller->InterruptMode = LevelSensitive;
} else {
Controller->SharableVector = FALSE;
Controller->InterruptMode = Latched;
}
Controller->SaveFloatState = FALSE;
Controller->RangeOfControllerBase = 8;
Controller->RangeOfControlPort = 1;
Controller->ControllerBaseAddress =
AtGetTranslatedMemory(
Controller->InterfaceType,
Controller->BusNumber,
Controller->OriginalControllerBaseAddress,
Controller->RangeOfControllerBase,
TRUE,
&Controller->ControllerBaseMapped
);
if (Controller->ControllerBaseAddress) {
Controller->ControlPortAddress =
AtGetTranslatedMemory(
Controller->InterfaceType,
Controller->BusNumber,
Controller->OriginalControlPortAddress,
Controller->RangeOfControlPort,
TRUE,
&Controller->ControlPortMapped
);
if (Controller->ControlPortAddress) {
Controller->ControllerVector = HalGetInterruptVector(
Controller->InterfaceType,
Controller->BusNumber,
Controller->OriginalControllerIrql,
Controller->OriginalControllerVector,
&Controller->ControllerIrql,
&Controller->ProcessorNumber );
} else {
returnValue = FALSE;
}
} else {
returnValue = FALSE;
}
}
if (parametersPath.Buffer) {
ExFreePool(parametersPath.Buffer);
}
if (parameters) {
ExFreePool(parameters);
}
return returnValue;
}
BOOLEAN
AtControllerPresent(
PCONTROLLER_DATA ControllerData
)
/*++
Routine Description:
This routine is used to determine if an AT controller exists.
Arguments:
ControllerData - Structure defining controller.
Return Value:
TRUE if controller exists.
--*/
{
//
// Write to indentifier to sector count register.
//
WRITE_PORT_UCHAR(ControllerData->ControllerBaseAddress + SECTOR_COUNT_REGISTER,
0xAA);
//
// Check if indentifier can be read back.
//
if (READ_PORT_UCHAR(ControllerData->ControllerBaseAddress + SECTOR_COUNT_REGISTER) == 0xAA) {
//
// Well there is a nasty scsi controller that likes to act like an IDE
// adapter. They shall remain nameless except their initials are
// DPT.
//
// So we don't detect this funky controller see if we can read the
// alternate status (which they foolishly neglected to implement).
// If it comes back as ff then assume that the controller doesn't
// exist.
//
#if 0
//
// Well we would have liked to leave this check in, but, GUESS WHAT!
// There are actually IDE drives (that shall remain nameless except
// that the first three letters of the line are MXT that don't answer
// the alternate status address.
//
if (READ_PORT_UCHAR(ControllerData->ControlPortAddress) == 0xff) {
return FALSE;
}
#endif
return TRUE;
} else {
return FALSE;
}
}
BOOLEAN
AtResetController(
IN PUCHAR StatusRegAddress,
IN PUCHAR DriveControlAddress,
IN CCHAR ControlFlags
)
/*++
Routine Description:
This routine will attempt to reset an atdisk controller.
Arguments:
StatusRegAddress - The address of the controllers status register.
DriveControlAddress - The address of the controllers drive control register.
ControlFlags - Values to OR into the the drive control register to
initialize with.
Return Value:
TRUE if controller successfully reset.
--*/
{
ULONG j;
WRITE_PORT_UCHAR(
DriveControlAddress,
RESET_CONTROLLER
);
//
// It shouldn't take it more than a tenth of a second to accept
// the reset command
//
if (!NT_SUCCESS(AtWaitControllerBusy(
StatusRegAddress,
10,
10000
))) {
return FALSE;
}
WRITE_PORT_UCHAR(
DriveControlAddress,
(UCHAR)(ENABLE_INTERRUPTS | ControlFlags)
);
for (
j = 0;
j < 500000;
j++
) {
if (READ_PORT_UCHAR(StatusRegAddress) == 0x50) {
return TRUE;
}
KeStallExecutionProcessor(10);
}
return FALSE;
}
VOID
AtDiskUpdateDeviceObjects(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine creates, deletes and changes device objects when
the IOCTL_SET_DRIVE_LAYOUT is called. It also updates the partition
number information in the structure that will be returned to the caller.
This routine can be used even in the GET_INFO case by insuring that
the RewritePartition flag is off for all partitions in the drive layout
structure.
Arguments:
DeviceObject - Device object for physical disk.
Irp - IO Request Packet (IRP).
Return Value:
None.
--*/
{
PDISK_EXTENSION part0DiskExtension = DeviceObject->DeviceExtension;
PDRIVE_LAYOUT_INFORMATION partitionList = Irp->AssociatedIrp.SystemBuffer;
ULONG partition;
ULONG partitionNumber;
ULONG partitionCount;
ULONG lastPartition;
ULONG partitionOrdinal;
PPARTITION_INFORMATION partitionEntry;
PPARTITION_EXTENSION partitionExtension;
BOOLEAN found;
//
// Works with a WINDISK has a feature where it is setting up the partition
// count "not as well as one would hope". This accounts for that feature.
//
partitionCount =
((partitionList->PartitionCount + 3) / 4) * 4;
//
// Walk through chain of partitions for this disk to determine
// which existing partitions have no match.
//
partitionExtension = (PVOID)part0DiskExtension;
lastPartition = 0;
//
// Zero all of the partition numbers.
//
for (partition = 0; partition < partitionCount; partition++) {
partitionEntry = &partitionList->PartitionEntry[partition];
partitionEntry->PartitionNumber = 0;
}
//
// Check if this is the last partition in the chain.
//
while (partitionExtension->NextPartition) {
partitionExtension = partitionExtension->NextPartition->DeviceExtension;
//
// Check for highest partition number this far.
//
if (partitionExtension->Pi.PartitionNumber > lastPartition) {
lastPartition = partitionExtension->Pi.PartitionNumber;
}
//
// Check if this partition is not currently being used.
//
if (partitionExtension->Pi.PartitionLength.QuadPart == 0) {
continue;
}
//
// Loop through partition information to look for match.
//
found = FALSE;
partitionOrdinal = 0;
for (partition = 0;
partition < partitionCount;
partition++) {
//
// Get partition descriptor.
//
partitionEntry = &partitionList->PartitionEntry[partition];
//
// Check if empty, or describes extended partiton or hasn't changed.
//
if (partitionEntry->PartitionType == PARTITION_ENTRY_UNUSED ||
IsContainerPartition(partitionEntry->PartitionType)) {
continue;
}
//
// Advance partition ordinal.
//
partitionOrdinal++;
//
// Check if new partition starts where this partition starts.
//
if (partitionEntry->StartingOffset.QuadPart !=
partitionExtension->Pi.StartingOffset.QuadPart) {
continue;
}
//
// Check if partition length is the same.
//
if (partitionEntry->PartitionLength.QuadPart ==
partitionExtension->Pi.PartitionLength.QuadPart) {
AtDump(
ATUPDATEDEVICE,
("ATDISK: AtDiskUpdateDeviceObjects: Found match for"
" \\Harddisk%d\\Partition%d\n",
part0DiskExtension->DiskNumber,
partitionExtension->Pi.PartitionNumber)
);
//
// Indicate match is found and set partition number
// in user buffer.
//
found = TRUE;
partitionEntry->PartitionNumber = partitionExtension->Pi.PartitionNumber;
partitionExtension->PartitionOrdinal = partitionOrdinal;
break;
}
}
if (found) {
//
// A match is found. If this partition is marked for update,
// check for a partition type change.
//
if (partitionEntry->RewritePartition) {
partitionExtension->Pi.PartitionType = partitionEntry->PartitionType;
}
} else {
//
// no match was found, indicate this partition is gone.
//
AtDump(
ATUPDATEDEVICE,
("ATDISK: AtDiskUpdateDeviceObject: Deleting "
"\\Device\\Harddisk%x\\Partition%x\n",
part0DiskExtension->DiskNumber,
partitionExtension->Pi.PartitionNumber)
);
partitionExtension->Pi.PartitionLength.QuadPart = 0;
}
}
//
// Walk through partition loop to find new partitions and set up
// device extensions to describe them. In some cases new device
// objects will be created.
//
partitionOrdinal = 0;
for (partition = 0;
partition < partitionCount;
partition++) {
//
// Get partition descriptor.
//
partitionEntry = &partitionList->PartitionEntry[partition];
//
// Check if empty or describes an extended partiton.
//
if (partitionEntry->PartitionType == PARTITION_ENTRY_UNUSED ||
IsContainerPartition(partitionEntry->PartitionType)) {
continue;
}
//
// Keep track of position on the disk for calls to IoSetPartitionInformation.
//
partitionOrdinal++;
//
// Check if this entry should be rewritten.
//
if (!partitionEntry->RewritePartition) {
continue;
}
if (partitionEntry->PartitionNumber) {
//
// Partition is an exact match with an existing partition, but is
// being written anyway.
//
continue;
}
//
// Check first if existing device object is available by
// walking partition extension list.
//
partitionNumber = 0;
partitionExtension = (PVOID)part0DiskExtension;
while (partitionExtension->NextPartition) {
partitionExtension = partitionExtension->NextPartition->DeviceExtension;
//
// A device object is free if the partition length is set to zero.
//
if (partitionExtension->Pi.PartitionLength.QuadPart == 0) {
partitionNumber = partitionExtension->Pi.PartitionNumber;
break;
}
}
//
// If partition number is still zero then a new device object
// must be created.
//
if (partitionNumber == 0) {
CCHAR ntNameBuffer[MAXIMUM_FILENAME_LENGTH];
STRING ntNameString;
UNICODE_STRING ntUnicodeString;
PDEVICE_OBJECT deviceObject;
NTSTATUS status;
lastPartition++;
partitionNumber = lastPartition;
//
// Get or create partition object and set up partition parameters.
//
sprintf(ntNameBuffer,
"\\Device\\Harddisk%d\\Partition%d",
part0DiskExtension->DiskNumber,
partitionNumber);
RtlInitString(&ntNameString,
ntNameBuffer);
status = RtlAnsiStringToUnicodeString(&ntUnicodeString,
&ntNameString,
TRUE);
if (!NT_SUCCESS(status)) {
continue;
}
AtDump(
ATUPDATEDEVICE,
("ATDISK: AtDiskUpdateDevice Create device object %s\n",
ntNameBuffer)
);
//
// This is a new name. Create the device object to represent it.
//
status = IoCreateDevice(DeviceObject->DriverObject,
sizeof(PARTITION_EXTENSION),
&ntUnicodeString,
FILE_DEVICE_DISK,
0,
FALSE,
&deviceObject);
if (!NT_SUCCESS(status)) {
AtDump(
ATUPDATEDEVICE,
("ATDISK: AtDiskUpdateDevice Can't create device %s\n",
ntNameBuffer)
);
RtlFreeUnicodeString(&ntUnicodeString);
continue;
}
RtlFreeUnicodeString(&ntUnicodeString);
//
// Set up device object fields.
//
deviceObject->Flags |= DO_DIRECT_IO;
deviceObject->StackSize = DeviceObject->StackSize;
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
//
// Link into the partition list.
//
partitionExtension->NextPartition = deviceObject;
//
// Set up device extension fields.
//
partitionExtension = deviceObject->DeviceExtension;
//
// Point back at device object.
//
partitionExtension->Partition0 = DeviceObject->DeviceExtension;
partitionExtension->NextPartition = NULL;
} else {
AtDump(
ATUPDATEDEVICE,
("ATDISK: AtDiskUpdateDevice Used existing device object"
" \\Device\\Harddisk%x\\Partition%x\n",
part0DiskExtension->DiskNumber,
partitionNumber)
);
}
//
// Write back partition number used in creating object name.
//
partitionEntry->PartitionNumber = partitionNumber;
partitionExtension->Pi = *partitionEntry;
partitionExtension->PartitionOrdinal = partitionOrdinal;
}
}
VOID
AtDiskTestPci(
IN OUT PCONTROLLER_DATA Controller
)
/*++
Routine Description:
Attempt to find this adapter in the PCI address space.
Arguments:
Controller - Structure that defines the "everything about the adapter.
Return Value:
None.
--*/
{
PCI_SLOT_NUMBER SlotNumber;
PPCI_COMMON_CONFIG PciData;
UCHAR buffer[PCI_COMMON_HDR_LENGTH];
ULONG i, f, j, bus;
BOOLEAN flag;
UCHAR vendorString[5] = {0};
UCHAR deviceString[5] = {0};
PciData = (PPCI_COMMON_CONFIG) buffer;
SlotNumber.u.bits.Reserved = 0;
flag = TRUE;
for (bus = 0; flag; bus++) {
for (i = 0; i < PCI_MAX_DEVICES && flag; i++) {
SlotNumber.u.bits.DeviceNumber = i;
for (f = 0; f < PCI_MAX_FUNCTION; f++) {
SlotNumber.u.bits.FunctionNumber = f;
j = HalGetBusData (
PCIConfiguration,
bus,
SlotNumber.u.AsULONG,
PciData,
PCI_COMMON_HDR_LENGTH
);
if (j == 0) {
// out of buses
flag = FALSE;
break;
}
if (PciData->VendorID == PCI_INVALID_VENDORID) {
// skip to next slot
break;
}
AtDump(
ATINIT,
("PciData: ------------------------\n"
" Bus: %d\n"
" Device: %d\n"
" Function: %d\n"
" Vendor Id: %x\n"
" Device Id: %x\n"
" Command: %x\n"
" Status: %x\n"
" Rev Id: %x\n"
" ProgIf: %x\n"
" SubClass: %x\n"
" BaseClass: %x\n"
" CacheLine: %x\n"
" Latency: %x\n"
" Header Type: %x\n"
" BIST: %x\n"
" Base Reg[0]: %x\n"
" Base Reg[1]: %x\n"
" Base Reg[2]: %x\n"
" Base Reg[3]: %x\n"
" Base Reg[4]: %x\n"
" Base Reg[5]: %x\n"
" Rom Base: %x\n"
" Interrupt Line: %x\n"
" Interrupt Pin: %x\n"
" Min Grant: %x\n"
" Max Latency: %x\n",
bus,
i,
f,
PciData->VendorID,
PciData->DeviceID,
PciData->Command,
PciData->Status,
PciData->RevisionID,
PciData->ProgIf,
PciData->SubClass,
PciData->BaseClass,
PciData->CacheLineSize,
PciData->LatencyTimer,
PciData->HeaderType,
PciData->BIST,
PciData->u.type0.BaseAddresses[0],
PciData->u.type0.BaseAddresses[1],
PciData->u.type0.BaseAddresses[2],
PciData->u.type0.BaseAddresses[3],
PciData->u.type0.BaseAddresses[4],
PciData->u.type0.BaseAddresses[5],
PciData->u.type0.ROMBaseAddress,
PciData->u.type0.InterruptLine,
PciData->u.type0.MinimumGrant,
PciData->u.type0.MaximumLatency)
);
if ((PciData->BaseClass == 1) &&
(PciData->SubClass == 1)) {
//
// Arrr, Here's a nasty boy.
//
//
// See if it's the controller that we are dealing with.
//
HalSetBusData (
PCIConfiguration,
bus,
SlotNumber.u.AsULONG,
PciData,
4
);
HalGetBusData (
PCIConfiguration,
bus,
SlotNumber.u.AsULONG,
PciData,
PCI_COMMON_HDR_LENGTH
);
if (PciData->u.type0.BaseAddresses[0] &
PCI_ADDRESS_IO_SPACE) {
if ((PciData->u.type0.BaseAddresses[0] & ~3) ==
(Controller->OriginalControllerBaseAddress.LowPart
& ~3)
) {
Controller->BadPciAdapter = TRUE;
return;
}
}
}
}
}
}
}
BOOLEAN
AtDiskIsPcmcia(
PPHYSICAL_ADDRESS Address,
PKIRQL Irql
)
/*++
Routine Description:
Look to see if this controller is described by the PCMCIA resource
tree in the registry.
Arguments:
ControllerData - Structure defining controller.
Return Value:
TRUE if found in the PCMCIA registry informaion.
FALSE otherwise.
--*/
{
UNICODE_STRING unicodeName;
OBJECT_ATTRIBUTES objectAttributes;
ULONG times;
HANDLE handle;
ULONG size;
PWCHAR wchar;
PUCHAR buffer;
NTSTATUS status;
ULONG rangeNumber;
ULONG index;
BOOLEAN match;
PKEY_VALUE_FULL_INFORMATION keyValueInformation;
PCM_FULL_RESOURCE_DESCRIPTOR fullResource;
PCM_PARTIAL_RESOURCE_DESCRIPTOR partialData;
buffer = ExAllocatePool(NonPagedPool, 2048);
if (!buffer) {
return FALSE;
}
unicodeName.Buffer = (PWSTR) buffer;
unicodeName.MaximumLength = (2048 / sizeof(WCHAR));
RtlInitUnicodeString(&unicodeName,
L"\\Registry\\Machine\\Hardware\\Description\\System\\PCMCIA PCCARDs");
InitializeObjectAttributes(&objectAttributes,
&unicodeName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
//
// Check for entry in DeviceMap
//
if (!NT_SUCCESS(ZwOpenKey(&handle, MAXIMUM_ALLOWED, &objectAttributes))) {
//
// Nothing there
//
ExFreePool(buffer);
return FALSE;
}
//
// See if key value for this driver is present
//
keyValueInformation = (PKEY_VALUE_FULL_INFORMATION) ExAllocatePool(NonPagedPool,
2048);
if (!keyValueInformation) {
ZwClose(handle);
ExFreePool(buffer);
return FALSE;
}
times = 2;
while (times) {
if (times == 2) {
RtlInitUnicodeString(&unicodeName, L"AtDisk");
} else {
RtlInitUnicodeString(&unicodeName, L"AtDisk1");
}
times--;
status = ZwQueryValueKey(handle,
&unicodeName,
KeyValueFullInformation,
keyValueInformation,
2048,
&size);
if ((!NT_SUCCESS(status)) || (!keyValueInformation->DataLength)) {
//
// No value present
//
break;
}
//
// Check to see if I/O port match.
//
fullResource = (PCM_FULL_RESOURCE_DESCRIPTOR)
((PUCHAR)keyValueInformation + keyValueInformation->DataOffset);
rangeNumber = 0;
match = FALSE;
for (index = 0; index < fullResource->PartialResourceList.Count; index++) {
partialData = &fullResource->PartialResourceList.PartialDescriptors[index];
switch (partialData->Type) {
case CmResourceTypePort:
if (partialData->u.Port.Start.LowPart == (ULONG) Address->LowPart) {
match = TRUE;
}
break;
}
}
if (match) {
//
// Search for the IRQL
//
for (index = 0; index < fullResource->PartialResourceList.Count; index++) {
partialData = &fullResource->PartialResourceList.PartialDescriptors[index];
switch (partialData->Type) {
case CmResourceTypeInterrupt:
*Irql = (UCHAR) partialData->u.Interrupt.Vector;
break;
default:
break;
}
}
ZwClose(handle);
ExFreePool(buffer);
ExFreePool(keyValueInformation);
return TRUE;
}
}
ZwClose(handle);
ExFreePool(buffer);
ExFreePool(keyValueInformation);
return FALSE;
}