windows-nt/Source/XPSP1/NT/base/busdrv/acpi/smbus/smbclass/smbcoprg.c

391 lines
11 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
smbcpnp.c
Abstract:
SMBus Class Driver Plug and Play support
Author:
Michael Hills
Environment:
Notes:
Revision History:
--*/
#include "smbc.h"
NTSTATUS
SmbCRawOpRegionCompletion (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
/*++
Routine Description:
This routine starts or continues servicing the device's work queue
Arguments:
DeviceObject - EC device object
Irp - Completing Irp
Context - Note used
Return Value:
Status
--*/
{
PACPI_OPREGION_CALLBACK completionHandler;
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation( Irp );
PVOID completionContext;
PFIELDUNITOBJ FieldUnit;
POBJDATA Data;
PBUFFERACC_BUFFER dataBuffer;
PSMB_REQUEST request;
ULONG i;
//
// Grab the arguments from the irp
//
completionHandler = (PACPI_OPREGION_CALLBACK) irpSp->Parameters.Others.Argument1;
completionContext = (PVOID) irpSp->Parameters.Others.Argument2;
FieldUnit = (PFIELDUNITOBJ) irpSp->Parameters.Others.Argument3;
Data = (POBJDATA) irpSp->Parameters.Others.Argument4;
SmbPrint(
SMB_HANDLER,
("SmbCRawOpRegionCompletion: Callback: %08lx Context: %08lx "
"Status: %08lx\n",
completionHandler, completionContext, Irp->IoStatus.Status )
);
//
// Copy the results into the buffer for a read.
//
request = (PSMB_REQUEST) Data->uipDataValue;
Data->uipDataValue = 0;
dataBuffer = (PBUFFERACC_BUFFER) Data->pbDataBuff;
dataBuffer->Status = request->Status;
switch (request->Protocol) {
case SMB_RECEIVE_BYTE:
case SMB_READ_BYTE:
case SMB_READ_WORD:
case SMB_READ_BLOCK:
case SMB_PROCESS_CALL:
case SMB_BLOCK_PROCESS_CALL:
//
// There is data to return
//
if (request->Status != SMB_STATUS_OK) {
SmbPrint(SMB_ERROR, ("SmbCRawOpRegionCompletion: SMBus error %x\n", request->Status));
dataBuffer->Length = 0xff;
RtlFillMemory (dataBuffer->Data, 32, 0xff);
} else {
if ((request->Protocol == SMB_READ_BLOCK) || (request->Protocol == SMB_BLOCK_PROCESS_CALL)) {
RtlCopyMemory (dataBuffer->Data, request->Data, request->BlockLength);
dataBuffer->Length = request->BlockLength;
} else {
*(PULONG)dataBuffer->Data = *((PULONG)(request->Data));
dataBuffer->Length = 0xff;
// This field is reseved for all but block accesses
}
}
}
//
// Invoke the AML interpreter's callback
//
(completionHandler)( completionContext);
//
// We are done with this irp
//
ExFreePool (request);
IoFreeIrp (Irp);
//
// Return this always --- we had to free the irp ourselves
//
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS EXPORT
SmbCRawOpRegionHandler (
ULONG AccessType,
PFIELDUNITOBJ FieldUnit,
POBJDATA Data,
ULONG_PTR Context,
PACPI_OPREGION_CALLBACK CompletionHandler,
PVOID CompletionContext
)
/*++
Routine Description:
This routine handles requests to service the EC operation region
Arguments:
AccessType - Read or Write data
FieldUnit - Opregion field info (address, command, protocol, etc.)
Data - Data Buffer
Context - SMBDATA
CompletionHandler - AMLI handler to call when operation is complete
CompletionContext - Context to pass to the AMLI handler
Return Value:
Status
Notes:
Could this be optimized by bypassing some of the IO subsystem?
--*/
{
NTSTATUS status;
PIRP irp = NULL;
PIO_STACK_LOCATION irpSp;
PSMBDATA smbData = (PSMBDATA) Context;
PSMB_REQUEST request = NULL;
PNSOBJ opRegion;
PBUFFERACC_BUFFER dataBuffer;
ULONG accType = FieldUnit->FieldDesc.dwFieldFlags & ACCTYPE_MASK;
ULONG i;
// DbgBreakPoint ();
SmbPrint(
SMB_HANDLER,
("SmbCRawOpRegionHandler: Entered - NSObj(%08x) ByteOfs(%08x) Start(%08x)"
" Num(%08x) Flags(%08x)\n",
FieldUnit->pnsFieldParent,
FieldUnit->FieldDesc.dwByteOffset,
FieldUnit->FieldDesc.dwStartBitPos,
FieldUnit->FieldDesc.dwNumBits,
FieldUnit->FieldDesc.dwFieldFlags)
);
//
// Parameter validation
//
if (accType != ACCTYPE_BUFFER) {
SmbPrint( SMB_ERROR, ("SmbCRawOpRegionHandler: Invalid Access type = 0x%08x should be ACCTYPE_BUFFER\n", accType) );
goto SmbCOpRegionHandlerError;
}
if (AccessType == ACPI_OPREGION_WRITE) {
if (Data->dwDataType != OBJTYPE_BUFFDATA) {
SmbPrint( SMB_ERROR, ("SmbCRawOpRegionHandler: Invalid dwDataType = 0x%08x should be OBJTYPE_BUFFDATA\n", Data->dwDataType) );
goto SmbCOpRegionHandlerError;
}
if (Data->dwDataLen != sizeof(BUFFERACC_BUFFER)) {
SmbPrint( SMB_ERROR, ("SmbCRawOpRegionHandler: Invalid dwDataLen = 0x%08x should be 0x%08x\n", Data->dwDataLen, sizeof(BUFFERACC_BUFFER)) );
goto SmbCOpRegionHandlerError;
}
} else if (AccessType == ACPI_OPREGION_READ) {
if ((Data->dwDataType != OBJTYPE_BUFFDATA) || (Data->pbDataBuff == NULL)) {
Data->dwDataType = OBJTYPE_INTDATA;
Data->dwDataValue = sizeof(BUFFERACC_BUFFER);
return STATUS_BUFFER_TOO_SMALL;
}
if (Data->dwDataLen != sizeof(BUFFERACC_BUFFER)) {
SmbPrint( SMB_ERROR, ("SmbCRawOpRegionHandler: Invalid dwDataLen = 0x%08x should be 0x%08x\n", Data->dwDataLen, sizeof(BUFFERACC_BUFFER)) );
goto SmbCOpRegionHandlerError;
}
} else {
SmbPrint( SMB_ERROR, ("SmbCRawOpRegionHandler: Invalid AccessType = 0x%08x\n", AccessType) );
goto SmbCOpRegionHandlerError;
}
//
// Allocate an IRP for below. Allocate one extra stack location to store
// some data in.
//
irp = IoAllocateIrp((CCHAR)(smbData->Class.DeviceObject->StackSize + 1),
FALSE
);
request = ExAllocatePoolWithTag (NonPagedPool, sizeof (SMB_REQUEST), 'CbmS');
if (!irp || !request) {
SmbPrint( SMB_ERROR, ("SmbCRawOpRegionHandler: Cannot allocate irp\n") );
goto SmbCOpRegionHandlerError;
}
//
// Fill in the top location so that we can use it ourselves
//
irpSp = IoGetNextIrpStackLocation( irp );
irpSp->Parameters.Others.Argument1 = (PVOID) CompletionHandler;
irpSp->Parameters.Others.Argument2 = (PVOID) CompletionContext;
irpSp->Parameters.Others.Argument3 = (PVOID) FieldUnit;
irpSp->Parameters.Others.Argument4 = (PVOID) Data;
IoSetNextIrpStackLocation( irp );
//
// Fill out the irp with the request info
//
irpSp = IoGetNextIrpStackLocation( irp );
irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
irpSp->Parameters.DeviceIoControl.IoControlCode = SMB_BUS_REQUEST;
irpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(SMB_REQUEST);
irpSp->Parameters.DeviceIoControl.Type3InputBuffer = request;
request->Status = 0;
//
// Translate between Opregion Protocols and smbus protocols
// and fill copy data.
//
//
// Copy data into data buffer for writes.
//
dataBuffer = (PBUFFERACC_BUFFER)Data->pbDataBuff;
if (AccessType == ACPI_OPREGION_WRITE) {
switch ((FieldUnit->FieldDesc.dwFieldFlags & FDF_ACCATTRIB_MASK) >> 8) {
case SMB_QUICK:
break;
case SMB_SEND_RECEIVE:
case SMB_BYTE:
*((PUCHAR) (request->Data)) = *((PUCHAR) (dataBuffer->Data));
break;
case SMB_WORD:
case SMB_PROCESS:
*((PUSHORT) (request->Data)) = *((PUSHORT) (dataBuffer->Data));
break;
case SMB_BLOCK:
case SMB_BLOCK_PROCESS:
dataBuffer = (PBUFFERACC_BUFFER)Data->pbDataBuff;
for (i = 0; i < dataBuffer->Length; i++) {
request->Data[i] = dataBuffer->Data[i];
}
request->BlockLength = (UCHAR) dataBuffer->Length;
break;
default:
SmbPrint( SMB_ERROR, ("SmbCRawOpRegionHandler: Invalid AccessAs: FieldFlags = 0x%08x\n", FieldUnit->FieldDesc.dwFieldFlags) );
goto SmbCOpRegionHandlerError;
}
}
//
// Determine protocol
//
request->Protocol = (UCHAR) ((FieldUnit->FieldDesc.dwFieldFlags & FDF_ACCATTRIB_MASK) >> 8);
if ((request->Protocol < SMB_QUICK) || (request->Protocol > SMB_BLOCK_PROCESS)) {
SmbPrint (SMB_ERROR, ("SmbCRawOpRegionHandler: BIOS BUG Unknown Protocol (access attribute) 0x%02x.\n", request->Protocol));
ASSERTMSG ("SmbCRawOpRegionHandler: Access type DWordAcc is not suported for SMB opregions.\n", FALSE);
goto SmbCOpRegionHandlerError;
}
if (request->Protocol <= SMB_BLOCK) {
request->Protocol -= (AccessType == ACPI_OPREGION_READ) ? 1 : 2;
} else {
request->Protocol -= 2;
}
SmbPrint(SMB_HANDLER,
("SmbCRawOpRegionHandler: request->Protocol = %08x\n", request->Protocol));
//
// Find the Slave address nd Command value (not used for all protocols)
//
request->Address = (UCHAR) ((FieldUnit->FieldDesc.dwByteOffset >> 8) & 0xff);
request->Command = (UCHAR) (FieldUnit->FieldDesc.dwByteOffset & 0xff);
//
// Pass Pointer to request in the data structure because
// there is not enough space in the irp stack.
// If this is a write, the data has already been copied out.
// If this is a read, we will read the value of request before
// copying the result data.
//
Data->uipDataValue = (ULONG_PTR) request;
//
// Set a completion routine
//
IoSetCompletionRoutine(
irp,
SmbCRawOpRegionCompletion,
NULL,
TRUE,
TRUE,
TRUE
);
//
// Send to the front-end of the SMB driver as a normal I/O request
//
status = IoCallDriver (smbData->Class.DeviceObject, irp);
if (!NT_SUCCESS(status)) {
SmbPrint (SMB_ERROR, ("SmbCRawOpRegionHandler: Irp failed with status %08x\n", status));
goto SmbCOpRegionHandlerError;
}
SmbPrint(
SMB_HANDLER,
("SmbCRawOpRegionHandler: Exiting - Data=%x Status=%x\n",
Data->uipDataValue, status)
);
return status;
SmbCOpRegionHandlerError:
if (irp) {
IoFreeIrp (irp);
}
if (request) {
ExFreePool (request);
}
Data->uipDataValue = 0xffffffff;
Data->dwDataLen = 0;
CompletionHandler( CompletionContext );
return STATUS_UNSUCCESSFUL;
}