Aktualisieren der Firmware für ein NVMe-Gerät
Updates für die Firmware auf einem NVMe-Speichergerät werden an den Miniporttreiber für dieses Gerät ausgegeben. Funktionsbefehle zum Abrufen von Firmwareinformationen, Herunterladen und Aktivieren von Firmwareimages werden an den Miniport ausgegeben.
Firmwareupgradeprozess
NVMe-Geräte, die für Windows zertifiziert sind, können ihre Firmware aktualisieren, während das Gerät ausgeführt wird. Die Firmware wird mithilfe der IOCTL_SCSI_MINIPORT Anforderung aktualisiert, die die zugehörigen Firmwaresteuerelementdaten enthält, die in einem SRB formatiert sind. Der Updatevorgang umfasst Folgendes:
Sammeln Sie die Informationen zum Firmwareplatz, um zu bestimmen, wo das Update platziert werden soll. Es gibt einige Überlegungen bei der Entscheidung, wo das Firmwareupdate platziert werden soll, z. B.:
- Wie viele Slots sind verfügbar?
- Wie viele Slots können ein Update enthalten? Einige Steckplätze sind schreibgeschützt oder halten Bilder, die beibehalten werden müssen, wenn die Möglichkeit zum Zurücksetzen auf ein vorheriges Bild gewünscht ist.
- Welcher Steckplatz enthält das aktuelle aktive Firmwareimage (die ausgeführte Firmware)?
Um das Gerät zu aktualisieren, wird ein Steckplatz ausgewählt, der schreibbar und derzeit nicht aktiv ist. Alle vorhandenen Bilddaten im ausgewählten Steckplatz werden überschrieben, wenn das Update abgeschlossen ist.
Laden Sie das neue Firmwareimage für einen ausgewählten Steckplatz herunter. Je nach Größe des Bilds erfolgt der Download in einem einzigen Übertragungsvorgang oder in aufeinander folgenden Übertragungen mehrerer Teile des Bilds. Ein Teil eines Bilds ist durch min(Controller Maximum Transfer Size, 512 KB) begrenzt.
Um das heruntergeladene Image zum aktiven Firmwareimage zu machen, wird seine Steckplatznummer dem Slot zugewiesen, in den es heruntergeladen wurde. Der aktive Firmware-Steckplatz wird dann vom aktuell verwendeten Steckplatz auf den Steckplatz umgestellt, der dem heruntergeladenen Bild zugewiesen ist. Abhängig vom Downloadtyp und den Änderungen am Firmwareimage ist möglicherweise ein Neustart des Systems erforderlich. Der NVMe-Controller bestimmt, ob ein Neustart erforderlich ist.
Miniport-Firmwaresteuerungsanforderungen
Jeder Funktionsbefehl wird in einer FIRMWARE_REQUEST_BLOCK Struktur festgelegt, die in einem SRB_IO_CONTROL im Puffer einer IOCTL_SCSI_MINIPORT Anforderung enthalten ist. Das ControlCode-Element von SRB_IO_CONTROL ist auf IOCTL_SCSI_MINIPORT_FIRMWARE festgelegt, um einen Miniport-Firmwarevorgang anzugeben. Jeder Funktionsbefehl verfügt über eine zugehörige Informationsstruktur, die sich nach dem FIRMWARE_REQUEST_BLOCK befindet. In der folgenden Tabelle sind die einzelnen Funktionsbefehle und die strukturen aufgeführt, die im Systempuffer für IOCTL_SCSI_MINIPORT enthalten sind.
Funktion | Eingabedaten | Ausgabedaten |
---|---|---|
FIRMWARE_FUNCTION_GET_INFO | SRB_IO_CONTROL + FIRMWARE_REQUEST_BLOCK | SRB_IO_CONTROL + FIRMWARE_REQUEST_BLOCK + STORAGE_FIRMWARE_SLOT_INFO |
FIRMWARE_FUNCTION_DOWNLOAD | SRB_IO_CONTROL + FIRMWARE_REQUEST_BLOCK + STORAGE_FIRMWARE_DOWNLOAD | SRB_IO_CONTROL |
FIRMWARE_FUNCTION_ACTIVATE | SRB_IO_CONTROL + FIRMWARE_REQUEST_BLOCK + STORAGE_FIRMWARE_ACTIVATE | SRB_IO_CONTROL |
Die Firmwarefunktionen und die zugehörigen Strukturen werden in ntddscsi.h definiert.
Informationen zum Firmwareplatz
Firmwareimages werden auf dem Gerät an Orten verwaltet, die als Steckplätze bezeichnet werden. Es ist erforderlich, einen verfügbaren Steckplatz für das Firmwareimage zu finden, der gespeichert werden soll, wenn das Firmwareimage nach einem Download aktiviert wird. Um einen verfügbaren Steckplatz zu finden, kann ein Upgradeprogramm eine Informationsabfrage an das Gerät senden, um die Platzinformationendeskriptoren zu empfangen. Die folgende Beispielfunktion zeigt, wie Die Informationen für alle Firmwaremodule auf einem ausgewählten NVMe-Gerät abgerufen werden.
// A device list item structure for an adapter
typedef struct _DEVICE_LIST {
HANDLE Handle;
STORAGE_ADAPTER_DESCRIPTOR AdapterDescriptor;
} DEVICE_LIST, *PDEVICE_LIST;
BOOL
DeviceGetFirmwareInfo(
_In_ PDEVICE_LIST DeviceList,
_In_ DWORD Index,
_Inout_ PUCHAR Buffer,
_In_ DWORD BufferLength,
_In_ BOOLEAN DisplayResult
)
/*++
Routine Description:
Retrieve the firmware and firmware slot information from NVMe controller.
Arguments:
DeviceList – a pointer to device array that contains disks information.
Index – the index of NVMe device in DeviceList array.
Buffer – a buffer for input and output.
BufferLength – the size of the buffer.
DisplayResult – print information on screen or not.
Return Value:
BOOLEAN
--*/
{
BOOL result;
ULONG returnedLength;
ULONG firmwareInfoOffset;
PSRB_IO_CONTROL srbControl;
PFIRMWARE_REQUEST_BLOCK firmwareRequest;
PSTORAGE_FIRMWARE_INFO firmwareInfo;
srbControl = (PSRB_IO_CONTROL)Buffer;
firmwareRequest = (PFIRMWARE_REQUEST_BLOCK)(srbControl + 1);
//
// The STORAGE_FIRMWARE_INFO is located after SRB_IO_CONTROL and FIRMWARE_REQUEST_BLOCK
//
firmwareInfoOffset = ((sizeof(SRB_IO_CONTROL) + sizeof(FIRMWARE_REQUEST_BLOCK) - 1) / sizeof(PVOID) + 1) * sizeof(PVOID);
//
// Setup the SRB control with the firmware ioctl control info
//
srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
srbControl->ControlCode = IOCTL_SCSI_MINIPORT_FIRMWARE;
RtlMoveMemory(srbControl->Signature, IOCTL_MINIPORT_SIGNATURE_FIRMWARE, 8);
srbControl->Timeout = 30;
srbControl->Length = BufferLength - sizeof(SRB_IO_CONTROL);
//
// Set firmware request fields for FIRMWARE_FUNCTION_GET_INFO. This request is to the controller so
// FIRMWARE_REQUEST_FLAG_CONTROLLER is set in the flags
//
firmwareRequest->Version = FIRMWARE_REQUEST_BLOCK_STRUCTURE_VERSION;
firmwareRequest->Size = sizeof(FIRMWARE_REQUEST_BLOCK);
firmwareRequest->Function = FIRMWARE_FUNCTION_GET_INFO;
firmwareRequest->Flags = FIRMWARE_REQUEST_FLAG_CONTROLLER;
firmwareRequest->DataBufferOffset = firmwareInfoOffset;
firmwareRequest->DataBufferLength = BufferLength - firmwareInfoOffset;
//
// Send the request to get the device firmware info
//
result = DeviceIoControl(DeviceList[Index].Handle,
IOCTL_SCSI_MINIPORT,
Buffer,
BufferLength,
Buffer,
BufferLength,
&returnedLength,
NULL
);
//
// Format and display the firmware info
//
if (DisplayResult) {
if (!result) {
_tprintf(_T("\t Get Firmware Information Failed: 0x%X\n"), GetLastError());
} else {
UCHAR i;
TCHAR revision[16] = {0};
firmwareInfo = (PSTORAGE_FIRMWARE_INFO)((PUCHAR)srbControl + firmwareRequest->DataBufferOffset);
_tprintf(_T("\t ----Firmware Information----\n"));
_tprintf(_T("\t Support upgrade command: %s\n"), firmwareInfo->UpgradeSupport ? _T("Yes") : _T("No"));
_tprintf(_T("\t Slot Count: %d\n"), firmwareInfo->SlotCount);
_tprintf(_T("\t Current Active Slot: %d\n"), firmwareInfo->ActiveSlot);
if (firmwareInfo->PendingActivateSlot == STORAGE_FIRMWARE_INFO_INVALID_SLOT) {
_tprintf(_T("\t Pending Active Slot: %s\n\n"), _T("No"));
} else {
_tprintf(_T("\t Pending Active Slot: %d\n\n"), firmwareInfo->PendingActivateSlot);
}
for (i = 0; i < firmwareInfo->SlotCount; i++) {
RtlCopyMemory(revision, &firmwareInfo->Slot[i].Revision.AsUlonglong, 8);
_tprintf(_T("\t\t Slot Number: %d\n"), firmwareInfo->Slot[i].SlotNumber);
_tprintf(_T("\t\t Slot Read Only: %s\n"), firmwareInfo->Slot[i].ReadOnly ? _T("Yes") : _T("No"));
_tprintf(_T("\t\t Revision: %s\n"), revision);
_tprintf(_T("\n"));
}
}
_tprintf(_T("\n"));
}
return result;
}
Slotinformationen werden in einem Array von STORAGE_FIRMWARE_SLOT_INFO Strukturen zurückgegeben. Jede Struktur gibt den Aktivierungsstatus und die Verfügbarkeit des Firmwareplatzes an. Verfügbarkeitsbedingungen sind:
- Das ReadOnly-Element ist auf 0 festgelegt.
- Der Steckplatz ist nicht der aktive Steckplatz, der durch die Steckplatznummer im ActiveSlot-Element von STORAGE_FIRMWARE_INFO angegeben ist.
- Das Element "PendingActiveSlot " von STORAGE_FIRMWARE_INFO ist auf STORAGE_FIRMWARE_INFO_INVALID_SLOT festgelegt.
- Das Element "PendingActiveSlot " von STORAGE_FIRMWARE_INFO ist nicht auf den gewünschten Steckplatz festgelegt.
Wenn der Slotstatus die Bedingungen für die Verfügbarkeit erfüllt, aber die Info-Zeichenfolge gültige Revisionsdaten enthält, die nichtzero Bytes sind, enthält der Steckplatz ein gültiges Firmwareimage, kann aber ersetzt werden. Alle Nullen in der Info-Zeichenfolge geben einen leeren Steckplatz an.
Beispiel: Firmwareupgrade – Slotauswahl, Download und Aktivierung
Ein Upgrade-Dienstprogramm führt die drei oben erwähnten Schritte aus, um die Firmware im Controller zu aktualisieren. Als Beispiel enthält die folgende Upgraderoutine Code für jeden Schritt im Prozess. Der Im Beispiel "DeviceGetFirmwareInfo" gezeigte Schritt "Slot Discovery" wird von der Upgraderoutine aufgerufen, um einen verfügbaren Steckplatz auszuwählen. Die Schritte zum Herunterladen und Aktivieren des Bilds werden direkt nach der Auswahl des Steckplatzes veranschaulicht. In jedem Schritt wird die Verwendung des entsprechenden Funktionsbefehls angezeigt.
Während des Downloadschritts wird eine Firmwareimagedatei in einen zugeordneten Puffer eingelesen, und der Pufferinhalt wird an den Controller übertragen. Wenn die Firmwareimagedatei größer als die Größe des Puffers ist, wird die Bilddatei mehrmals mit einem Teil des Bilds gelesen, der jedes Mal übertragen wird, bis die gesamte Datei gelesen wird.
Nach Abschluss des Firmwareimagedownloads erfordert der Aktivierungsschritt zwei Aktionen vom Controller. Zuerst wird dem Firmwareimage der ausgewählte Steckplatz zugewiesen, und zweitens wird der ausgewählte Steckplatz als aktiver Steckplatz festgelegt.
VOID
DeviceFirmwareUpgrade(
_In_ PDEVICE_LIST DeviceList,
_In_ DWORD Index,
_In_ TCHAR* FileName
)
/*++
Routine Description:
Performs a firmware upgrade to the NVMe controller. The an available firmware
slot is selected, the firmware is downloaded to the controller from an image
file, and the new firmware is activated.
Arguments:
DeviceList – a pointer to device array that contains disks information.
Index – the index of NVMe device in DeviceList array.
FileName – the name of the firmware upgrade image file.
Return Value:
None
--*/
{
BOOL result;
PUCHAR buffer = NULL;
ULONG bufferSize;
ULONG firmwareStructureOffset;
ULONG imageBufferLength;
PSRB_IO_CONTROL srbControl;
PFIRMWARE_REQUEST_BLOCK firmwareRequest;
PSTORAGE_FIRMWARE_INFO firmwareInfo;
PSTORAGE_FIRMWARE_DOWNLOAD firmwareDownload;
PSTORAGE_FIRMWARE_ACTIVATE firmwareActivate;
ULONG slotNumber;
ULONG returnedLength;
ULONG i;
HANDLE fileHandle = NULL;
ULONG imageOffset;
ULONG readLength;
BOOLEAN moreToDownload;
//
// The STORAGE_FIRMWARE_INFO is located after SRB_IO_CONTROL and FIRMWARE_RESQUEST_BLOCK
//
firmwareStructureOffset = ((sizeof(SRB_IO_CONTROL) + sizeof(FIRMWARE_REQUEST_BLOCK) - 1) / sizeof(PVOID) + 1) * sizeof(PVOID);
//
// The Max Transfer Length limits the part of buffer that may need to transfer to controller, not the whole buffer.
//
bufferSize = min(DeviceList[Index].AdapterDescriptor.MaximumTransferLength, 2 * 1024 * 1024);
bufferSize += firmwareStructureOffset;
bufferSize += FIELD_OFFSET(STORAGE_FIRMWARE_DOWNLOAD, ImageBuffer);
buffer = (PUCHAR)malloc(bufferSize);
if (buffer == NULL) {
_tprintf(_T("\t FirmwareUpgrade - Allocate buffer failed: 0x%X\n"), GetLastError());
return;
}
//
// calculate the space available for the firmware image portion of the buffer allocation
//
imageBufferLength = bufferSize - firmwareStructureOffset - sizeof(STORAGE_FIRMWARE_DOWNLOAD);
RtlZeroMemory(buffer, bufferSize);
// ---------------------------------------------------------------------------
// ( 1 ) SELECT A SUITABLE FIRMWARE SLOT
// ---------------------------------------------------------------------------
//
// Get firmware slot information data.
//
result = DeviceGetFirmwareInfo(DeviceList, Index, buffer, bufferSize, FALSE);
if (result == FALSE) {
_tprintf(_T("\t FirmwareUpgrade: Get Firmware Information Failed: 0x%X\n"), GetLastError());
goto Exit;
}
//
// Set the request structure pointers
//
srbControl = (PSRB_IO_CONTROL)buffer;
firmwareRequest = (PFIRMWARE_REQUEST_BLOCK)(srbControl + 1);
firmwareInfo = (PSTORAGE_FIRMWARE_INFO)((PUCHAR)srbControl + firmwareRequest->DataBufferOffset);
if (srbControl->ReturnCode != FIRMWARE_STATUS_SUCCESS) {
_tprintf(_T("\t FirmwareUpgrade - get firmware info failed. srbControl->ReturnCode %d.\n"), srbControl->ReturnCode);
goto Exit;
}
//
// SelectFind the first writable slot.
//
slotNumber = (ULONG)-1;
if (firmwareInfo->UpgradeSupport) {
for (i = 0; i < firmwareInfo->SlotCount; i++) {
if (firmwareInfo->Slot[i].ReadOnly == FALSE) {
slotNumber = firmwareInfo->Slot[i].SlotNumber;
break;
}
}
}
//
// If no writable slot is found, bypass downloading and activation
//
if (slotNumber == (ULONG)-1) {
_tprintf(_T("\t FirmwareUpgrade - No writable Firmware slot.\n"));
goto Exit;
}
// ---------------------------------------------------------------------------
// ( 2 ) DOWNLOAD THE FIRMWARE IMAGE TO THE CONTROLLER
// ---------------------------------------------------------------------------
//
// initialize image length and offset
//
imageBufferLength = (imageBufferLength / sizeof(PVOID)) * sizeof(PVOID);
imageOffset = 0;
readLength = 0;
moreToDownload = TRUE;
//
// Open image file and download it to controller.
//
if (FileName == NULL) {
_tprintf(_T("\t FirmwareUpgrade - No firmware file specified.\n"));
goto Exit;
}
fileHandle = CreateFile(FileName, // file to open
GENERIC_READ, // open for reading
FILE_SHARE_READ, // share for reading
NULL, // default security
OPEN_EXISTING, // existing file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
if (fileHandle == INVALID_HANDLE_VALUE) {
_tprintf(_T("\t FirmwareUpgrade - unable to open file \"%s\" for read.\n"), FileName);
goto Exit;
}
//
// Read and download the firmware from the image file into image buffer length portions. Send the
// image portion to the controller.
//
while (moreToDownload) {
RtlZeroMemory(buffer, bufferSize);
//
// Setup the SRB control with the firmware ioctl control info
//
srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
srbControl->ControlCode = IOCTL_SCSI_MINIPORT_FIRMWARE;
RtlMoveMemory(srbControl->Signature, IOCTL_MINIPORT_SIGNATURE_FIRMWARE, 8);
srbControl->Timeout = 30;
srbControl->Length = bufferSize - sizeof(SRB_IO_CONTROL);
//
// Set firmware request fields for FIRMWARE_FUNCTION_DOWNLOAD. This request is to the controller so
// FIRMWARE_REQUEST_FLAG_CONTROLLER is set in the flags
//
firmwareRequest->Version = FIRMWARE_REQUEST_BLOCK_STRUCTURE_VERSION;
firmwareRequest->Size = sizeof(FIRMWARE_REQUEST_BLOCK);
firmwareRequest->Function = FIRMWARE_FUNCTION_DOWNLOAD;
firmwareRequest->Flags = FIRMWARE_REQUEST_FLAG_CONTROLLER;
firmwareRequest->DataBufferOffset = firmwareStructureOffset;
firmwareRequest->DataBufferLength = bufferSize - firmwareStructureOffset;
//
// Initialize the firmware data buffer pointer to the proper position after the request structure
//
firmwareDownload = (PSTORAGE_FIRMWARE_DOWNLOAD)((PUCHAR)srbControl + firmwareRequest->DataBufferOffset);
if (ReadFile(fileHandle, firmwareDownload->ImageBuffer, imageBufferLength, &readLength, NULL) == FALSE) {
_tprintf(_T("\t FirmwareUpgrade - Read firmware file failed.\n"));
goto Exit;
}
if (readLength == 0) {
moreToDownload = FALSE;
break;
}
if ((readLength % sizeof(ULONG)) != 0) {
_tprintf(_T("\t FirmwareUpgrade - Read firmware file failed.\n"));
}
//
// Set the download parameters and adjust the offset for this portion of the firmware image
//
firmwareDownload->Version = 1;
firmwareDownload->Size = sizeof(STORAGE_FIRMWARE_DOWNLOAD);
firmwareDownload->Offset = imageOffset;
firmwareDownload->BufferSize = readLength;
//
// download this portion of firmware to the device
//
result = DeviceIoControl(DeviceList[Index].Handle,
IOCTL_SCSI_MINIPORT,
buffer,
bufferSize,
buffer,
bufferSize,
&returnedLength,
NULL
);
if (result == FALSE) {
_tprintf(_T("\t FirmwareUpgrade - IOCTL - firmware download failed. 0x%X.\n"), GetLastError());
goto Exit;
}
if (srbControl->ReturnCode != FIRMWARE_STATUS_SUCCESS) {
_tprintf(_T("\t FirmwareUpgrade - firmware download failed. srbControl->ReturnCode %d.\n"), srbControl->ReturnCode);
goto Exit;
}
//
// Update Image Offset for next iteration.
//
imageOffset += readLength;
}
// ---------------------------------------------------------------------------
// ( 3 ) ACTIVATE THE FIRMWARE SLOT ASSIGNED TO THE UPGRADE
// ---------------------------------------------------------------------------
//
// Activate the newly downloaded image with the assigned slot.
//
RtlZeroMemory(buffer, bufferSize);
//
// Setup the SRB control with the firmware ioctl control info
//
srbControl->HeaderLength = sizeof(SRB_IO_CONTROL);
srbControl->ControlCode = IOCTL_SCSI_MINIPORT_FIRMWARE;
RtlMoveMemory(srbControl->Signature, IOCTL_MINIPORT_SIGNATURE_FIRMWARE, 8);
srbControl->Timeout = 30;
srbControl->Length = bufferSize - sizeof(SRB_IO_CONTROL);
//
// Set firmware request fields for FIRMWARE_FUNCTION_ACTIVATE. This request is to the controller so
// FIRMWARE_REQUEST_FLAG_CONTROLLER is set in the flags
//
firmwareRequest->Version = FIRMWARE_REQUEST_BLOCK_STRUCTURE_VERSION;
firmwareRequest->Size = sizeof(FIRMWARE_REQUEST_BLOCK);
firmwareRequest->Function = FIRMWARE_FUNCTION_ACTIVATE;
firmwareRequest->Flags = FIRMWARE_REQUEST_FLAG_CONTROLLER;
firmwareRequest->DataBufferOffset = firmwareStructureOffset;
firmwareRequest->DataBufferLength = bufferSize - firmwareStructureOffset;
//
// Initialize the firmware activation structure pointer to the proper position after the request structure
//
firmwareActivate = (PSTORAGE_FIRMWARE_ACTIVATE)((PUCHAR)srbControl + firmwareRequest->DataBufferOffset);
//
// Set the activation parameters with the available slot selected
//
firmwareActivate->Version = 1;
firmwareActivate->Size = sizeof(STORAGE_FIRMWARE_ACTIVATE);
firmwareActivate->SlotToActivate = (UCHAR)slotNumber;
//
// Send the activation request
//
result = DeviceIoControl(DeviceList[Index].Handle,
IOCTL_SCSI_MINIPORT,
buffer,
bufferSize,
buffer,
bufferSize,
&returnedLength,
NULL
);
if (result == FALSE) {
_tprintf(_T("\t FirmwareUpgrade - IOCTL - firmware activate failed. 0x%X.\n"), GetLastError());
goto Exit;
}
//
// Display status result from firmware activation
//
switch (srbControl->ReturnCode) {
case FIRMWARE_STATUS_SUCCESS:
_tprintf(_T("\t FirmwareUpgrade - firmware activate succeeded.\n"));
break;
case FIRMWARE_STATUS_POWER_CYCLE_REQUIRED:
_tprintf(_T("\t FirmwareUpgrade - firmware activate succeeded. PLEASE REBOOT COMPUTER.\n"));
break;
case FIRMWARE_STATUS_ILLEGAL_REQUEST:
case FIRMWARE_STATUS_INVALID_PARAMETER:
case FIRMWARE_STATUS_INPUT_BUFFER_TOO_BIG:
_tprintf(_T("\t FirmwareUpgrade - firmware activate parameter error. srbControl->ReturnCode %d.\n"), srbControl->ReturnCode);
break;
case FIRMWARE_STATUS_INVALID_SLOT:
_tprintf(_T("\t FirmwareUpgrade - firmware activate, slot number invalid.\n"));
break;
case FIRMWARE_STATUS_INVALID_IMAGE:
_tprintf(_T("\t FirmwareUpgrade - firmware activate, invalid firmware image.\n"));
break;
case FIRMWARE_STATUS_ERROR:
case FIRMWARE_STATUS_CONTROLLER_ERROR:
_tprintf(_T("\t FirmwareUpgrade - firmware activate, error returned.\n"));
break;
default:
_tprintf(_T("\t FirmwareUpgrade - firmware activate, unexpected error. srbControl->ReturnCode %d.\n"), srbControl->ReturnCode);
break;
}
Exit:
if (fileHandle != NULL) {
CloseHandle(fileHandle);
}
if (buffer != NULL) {
free(buffer);
}
return;
}
Hinweis
Das gleichzeitige Herunterladen mehrerer Firmwareimages wird nicht unterstützt. Auf einen einzelnen Firmwaredownload folgt immer eine einzelne Firmwareaktivierung.
Ein Firmwareimage, das bereits in einem Steckplatz vorhanden ist, kann mithilfe des Befehls "Funktion aktivieren" mit der entsprechenden Steckplatznummer reaktiviert werden.
Der IOCTL_SCSI_MINIPORT_FIRMWARE Steuerelementcode für das SRB-E/A-Steuerelement ist ab Windows 8.1 verfügbar.