|
|
/*++
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; }
|