391 lines
11 KiB
C
391 lines
11 KiB
C
/*++
|
||
|
||
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;
|
||
}
|
||
|
||
|