1070 lines
28 KiB
C
1070 lines
28 KiB
C
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
smbali.c
|
|
|
|
Abstract:
|
|
|
|
SMB Host Controller Driver for ALI chipset
|
|
|
|
Author:
|
|
|
|
Michael Hills
|
|
|
|
Environment:
|
|
|
|
Notes:
|
|
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "smbalip.h"
|
|
|
|
#if DBG
|
|
ULONG SmbAliDebug = SMB_ERROR|SMB_ALARM;
|
|
ULONG DbgSuccess = 0;
|
|
|
|
ULONG DbgFailure = 0;
|
|
ULONG DbgAddrNotAck = 0;
|
|
ULONG DbgTimeOut = 0;
|
|
ULONG DbgOtherErr = 0;
|
|
#endif
|
|
|
|
LARGE_INTEGER SmbIoPollRate = {-20*MILLISECONDS, -1}; // 20 millisecond poll rate. Relative time, so has to be negative
|
|
ULONG SmbIoInitTimeOut = 15; // 15 IoPollRate intervals before timeout
|
|
ULONG SmbIoCompleteTimeOut = 20; // 20 IoPollRate intervals before timeout
|
|
|
|
|
|
NTSTATUS
|
|
DriverEntry (
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the SMBus Host Controller Driver
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to driver object created by system.
|
|
RegistryPath - Pointer to the Unicode name of the registry path
|
|
for this driver.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status from the initialization operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
// DbgBreakPoint();
|
|
|
|
status = SmbClassInitializeDevice (
|
|
SMB_ALI_MAJOR_VERSION,
|
|
SMB_ALI_MINOR_VERSION,
|
|
DriverObject
|
|
);
|
|
DriverObject->DriverExtension->AddDevice = SmbAliAddDevice;
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbAliInitializeMiniport (
|
|
IN PSMB_CLASS SmbClass,
|
|
IN PVOID MiniportExtension,
|
|
IN PVOID MiniportContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine Initializes miniport data, and sets up communication with
|
|
lower device objects.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to driver object created by system.
|
|
Pdo - Pointer to physical device object
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
STATUS_INSUFFICIENT_RESOURCES
|
|
|
|
--*/
|
|
{
|
|
PSMB_ALI_DATA AliData = (PSMB_ALI_DATA) MiniportExtension;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
KEVENT syncEvent;
|
|
|
|
|
|
AliData->IoState = SmbIoIdle;
|
|
|
|
//
|
|
// Fill in SmbClass info
|
|
//
|
|
|
|
SmbClass->StartIo = SmbAliStartIo;
|
|
SmbClass->ResetDevice = SmbAliResetDevice;
|
|
SmbClass->StopDevice = SmbAliStopDevice;
|
|
|
|
//
|
|
// Get Acpi Interfaces
|
|
//
|
|
|
|
//
|
|
// Allocate an IRP for below
|
|
//
|
|
irp = IoAllocateIrp (SmbClass->LowerDeviceObject->StackSize, FALSE);
|
|
|
|
if (!irp) {
|
|
SmbPrint((SMB_ERROR),
|
|
("SmbAliInitializeMiniport: Failed to allocate Irp\n"));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
|
|
//
|
|
// Use QUERY_INTERFACE to get the address of the direct-call ACPI interfaces.
|
|
//
|
|
irpSp->MajorFunction = IRP_MJ_PNP;
|
|
irpSp->MinorFunction = IRP_MN_QUERY_INTERFACE;
|
|
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
|
|
irpSp->Parameters.QueryInterface.InterfaceType = (LPGUID) &GUID_ACPI_INTERFACE_STANDARD;
|
|
irpSp->Parameters.QueryInterface.Version = 1;
|
|
irpSp->Parameters.QueryInterface.Size = sizeof (AliData->AcpiInterfaces);
|
|
irpSp->Parameters.QueryInterface.Interface = (PINTERFACE) &AliData->AcpiInterfaces;
|
|
irpSp->Parameters.QueryInterface.InterfaceSpecificData = NULL;
|
|
|
|
//
|
|
// Initialize an event so this will be a syncronous call.
|
|
//
|
|
|
|
KeInitializeEvent(&syncEvent, SynchronizationEvent, FALSE);
|
|
|
|
IoSetCompletionRoutine (irp, SmbAliSyncronousIrpCompletion, &syncEvent, TRUE, TRUE, TRUE);
|
|
|
|
//
|
|
// Call ACPI
|
|
//
|
|
|
|
status = IoCallDriver (SmbClass->LowerDeviceObject, irp);
|
|
|
|
//
|
|
// Wait if necessary, then clean up.
|
|
//
|
|
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&syncEvent, Executive, KernelMode, FALSE, NULL);
|
|
status = irp->IoStatus.Status;
|
|
}
|
|
|
|
IoFreeIrp (irp);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
SmbPrint(SMB_ERROR,
|
|
("SmbAliInitializeMiniport: Could not get ACPI driver interfaces, status = %x\n", status));
|
|
}
|
|
|
|
//
|
|
// Initiaize worker thread
|
|
//
|
|
AliData->WorkItem = IoAllocateWorkItem (SmbClass->LowerDeviceObject);
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbAliAddDevice (
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT Pdo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine calls SMBClassCreateFdo to create the FDO
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Pointer to driver object created by system.
|
|
Pdo - Pointer to physical device object
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
STATUS_INSUFFICIENT_RESOURCES
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT fdo = NULL;
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
SmbPrint(SMB_TRACE, ("SmbAliAddDevice Entered with pdo %x\n", Pdo));
|
|
|
|
|
|
if (Pdo == NULL) {
|
|
|
|
//
|
|
// Have we been asked to do detection on our own?
|
|
// if so just return no more devices
|
|
//
|
|
|
|
SmbPrint(SMB_ERROR, ("SmbHcAddDevice - asked to do detection\n"));
|
|
return STATUS_NO_MORE_ENTRIES;
|
|
}
|
|
|
|
//
|
|
// Create and initialize the new functional device object
|
|
//
|
|
|
|
status = SmbClassCreateFdo(
|
|
DriverObject,
|
|
Pdo,
|
|
sizeof (SMB_ALI_DATA),
|
|
SmbAliInitializeMiniport,
|
|
NULL,
|
|
&fdo
|
|
);
|
|
|
|
if (!NT_SUCCESS(status) || fdo == NULL) {
|
|
SmbPrint(SMB_ERROR, ("SmbAliAddDevice - error creating Fdo. Status = %08x\n", status));
|
|
}
|
|
|
|
return status;
|
|
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbAliResetDevice (
|
|
IN struct _SMB_CLASS* SmbClass,
|
|
IN PVOID SmbMiniport
|
|
)
|
|
{
|
|
PCM_FULL_RESOURCE_DESCRIPTOR fullResourceDesc;
|
|
PCM_PARTIAL_RESOURCE_LIST partialResourceList;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR partialResourceDesc;
|
|
ULONG i;
|
|
PIO_STACK_LOCATION irpStack;
|
|
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
PSMB_ALI_DATA AliData = (PSMB_ALI_DATA) SmbMiniport;
|
|
|
|
PAGED_CODE();
|
|
|
|
|
|
if (SmbClass->CurrentIrp == NULL) {
|
|
SmbPrint(SMB_ERROR, ("SmbAliResetDevice: Null CurrentIrp. Can't get Alloocated Resources.\n"));
|
|
|
|
return STATUS_NO_MORE_ENTRIES;
|
|
}
|
|
irpStack = IoGetCurrentIrpStackLocation(SmbClass->CurrentIrp);
|
|
|
|
if (irpStack->Parameters.StartDevice.AllocatedResources == NULL) {
|
|
SmbPrint(SMB_ERROR, ("SmbAliResetDevice: Null resource pointer\n"));
|
|
|
|
return STATUS_NO_MORE_ENTRIES;
|
|
}
|
|
|
|
if (irpStack->Parameters.StartDevice.AllocatedResources->Count <= 0 ) {
|
|
SmbPrint(SMB_ERROR, ("SmbAliResetDevice: Count <= 0\n"));
|
|
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Traverse the resource list
|
|
//
|
|
|
|
AliData->SmbBaseIo = NULL;
|
|
|
|
fullResourceDesc=&irpStack->Parameters.StartDevice.AllocatedResources->List[0];
|
|
partialResourceList = &fullResourceDesc->PartialResourceList;
|
|
partialResourceDesc = partialResourceList->PartialDescriptors;
|
|
|
|
for (i=0; i<partialResourceList->Count; i++, partialResourceDesc++) {
|
|
|
|
if (partialResourceDesc->Type == CmResourceTypePort) {
|
|
|
|
if (AliData->SmbBaseIo == NULL) {
|
|
AliData->SmbBaseIo = (PUCHAR)((ULONG_PTR)partialResourceDesc->u.Port.Start.LowPart);
|
|
if (partialResourceDesc->u.Port.Length != SMB_ALI_IO_RESOURCE_LENGTH) {
|
|
SmbPrint(SMB_ERROR, ("SmbAliResetDevice: Wrong Resource length = 0x%08x\n", partialResourceDesc->u.Port.Length));
|
|
DbgBreakPoint();
|
|
}
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
SmbPrint(SMB_ERROR, ("SmbAliResetDevice: More than 1 IO resource. Resources = 0x%08x\n", irpStack->Parameters.StartDevice.AllocatedResources));
|
|
DbgBreakPoint();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
SmbPrint(SMB_ERROR, ("SmbAliResetDevice: IO resource error. Resources = 0x%08x\n", irpStack->Parameters.StartDevice.AllocatedResources));
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
SmbPrint(SMB_TRACE, ("SmbAliResetDevice: IO Address = 0x%08x\n", AliData->SmbBaseIo));
|
|
|
|
//
|
|
// Register for device notification
|
|
//
|
|
// But this device can't seem to notify so never mind for now
|
|
//status = AliData->AcpiInterfaces.RegisterForDeviceNotifications (
|
|
// AliData->AcpiInterfaces.Context,
|
|
// SmbAliNotifyHandler,
|
|
// AliData);
|
|
//
|
|
//if (!NT_SUCCESS(status)) {
|
|
// SmbPrint(SMB_ERROR, ("SmbAliResetDevice: Failed RegisterForDeviceNotification. 0x%08x\n", status));
|
|
//}
|
|
|
|
KeInitializeTimer (&AliData->InitTimer);
|
|
KeInitializeDpc (&AliData->InitDpc,
|
|
SmbAliInitTransactionDpc,
|
|
SmbClass);
|
|
AliData->InitWorker = IoAllocateWorkItem (SmbClass->DeviceObject);
|
|
|
|
KeInitializeTimer (&AliData->CompleteTimer);
|
|
KeInitializeDpc (&AliData->CompleteDpc,
|
|
SmbAliCompleteTransactionDpc,
|
|
SmbClass);
|
|
AliData->CompleteWorker = IoAllocateWorkItem (SmbClass->DeviceObject);
|
|
|
|
SmbAliStartDevicePolling (SmbClass);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
SmbAliStopDevice (
|
|
IN struct _SMB_CLASS* SmbClass,
|
|
IN PSMB_ALI_DATA AliData
|
|
)
|
|
{
|
|
SmbAliStopDevicePolling (SmbClass);
|
|
|
|
AliData->SmbBaseIo = NULL;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
SmbAliStartIo (
|
|
IN struct _SMB_CLASS* SmbClass,
|
|
IN PSMB_ALI_DATA AliData
|
|
)
|
|
|
|
{
|
|
SmbPrint (SMB_TRACE, ("SmbAliStartIo: \n"));
|
|
|
|
SmbPrint (SMB_IO_REQUEST, (" Prtcl = %02x Addr = %02x Cmd = %02x BlkLen = %02x Data[0] = %02x\n",
|
|
SmbClass->CurrentSmb->Protocol,
|
|
SmbClass->CurrentSmb->Address,
|
|
SmbClass->CurrentSmb->Command,
|
|
SmbClass->CurrentSmb->BlockLength,
|
|
SmbClass->CurrentSmb->Data[0]));
|
|
// KeSetTimer (&AliData->InitTimer,
|
|
// Smb100ns,
|
|
// &AliData->InitDpc);
|
|
|
|
AliData->InternalRetries = 0;
|
|
|
|
AliData->InitTimeOut = SmbIoInitTimeOut;
|
|
IoQueueWorkItem (AliData->InitWorker,
|
|
SmbAliInitTransactionWorker,
|
|
DelayedWorkQueue,
|
|
SmbClass);
|
|
|
|
}
|
|
|
|
VOID
|
|
SmbAliInitTransactionDpc (
|
|
IN struct _KDPC *Dpc,
|
|
IN struct _SMB_CLASS* SmbClass,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
{
|
|
PSMB_ALI_DATA AliData = (PSMB_ALI_DATA)(SmbClass->Miniport);
|
|
|
|
IoQueueWorkItem (AliData->InitWorker,
|
|
SmbAliInitTransactionWorker,
|
|
DelayedWorkQueue,
|
|
SmbClass);
|
|
}
|
|
|
|
VOID
|
|
SmbAliInitTransactionWorker (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN struct _SMB_CLASS* SmbClass
|
|
)
|
|
{
|
|
PSMB_ALI_DATA AliData = (PSMB_ALI_DATA)(SmbClass->Miniport);
|
|
UCHAR address;
|
|
UCHAR protocol;
|
|
|
|
SmbPrint (SMB_TRACE, ("SmbAliInitTransaction: Entered \n"));
|
|
|
|
if (SmbClass->CurrentSmb->Protocol >= SMB_MAXIMUM_PROTOCOL) {
|
|
SmbClass->CurrentSmb->Status = SMB_UNSUPPORTED_PROTOCOL;
|
|
// REVIEW: Shouldn't this complete the request? jimmat
|
|
return;
|
|
}
|
|
|
|
if (SmbAliHostBusy(AliData)) {
|
|
if (AliData->InitTimeOut == 4) {
|
|
// Time out. Issue kill command. If that fixes it, good, otherwise
|
|
// issue bus timeout command next time.
|
|
SmbAliResetHost (AliData);
|
|
}
|
|
if (AliData->InitTimeOut == 0) {
|
|
// Time out. Issue Bus timeout and kill command to reset host.
|
|
SmbAliResetBus (AliData);
|
|
|
|
AliData->InitTimeOut = SmbIoInitTimeOut;
|
|
} else {
|
|
SmbPrint (SMB_TRACE, ("SmbAliInitTransaction: Waiting (%d) \n", AliData->InitTimeOut));
|
|
AliData->InitTimeOut--;
|
|
}
|
|
KeSetTimer (&AliData->InitTimer,
|
|
SmbIoPollRate,
|
|
&AliData->InitDpc);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Ready to go
|
|
//
|
|
|
|
// Set Address and read/write bit
|
|
address = SmbClass->CurrentSmb->Address << 1 | (SmbClass->CurrentSmb->Protocol & 1);
|
|
SmbPrint (SMB_IO, ("SmbAliInitTransaction: IO write DEV_ADDR = 0x%02x \n", address));
|
|
WRITE_PORT_UCHAR (DEV_ADDR_REG, address);
|
|
|
|
SMBDELAY;
|
|
|
|
// Set transaction type: Inserts bits 3-1 of protocol into bits 6-4 of SMB_TYP
|
|
// protocol = READ_PORT_UCHAR (SMB_TYP_REG);
|
|
// SmbPrint (SMB_IO, ("SmbAliInitTransaction: IO read SMB_TYP = 0x%02x \n", protocol));
|
|
protocol = /*(protocol & ~SMB_TYP_MASK) |*/
|
|
((SmbClass->CurrentSmb->Protocol << 3) & SMB_TYP_MASK);
|
|
SmbPrint (SMB_IO, ("SmbAliInitTransaction: IO write SMB_TYP = 0x%02x, Protocol = 0x%02x \n", protocol,SmbClass->CurrentSmb->Protocol));
|
|
WRITE_PORT_UCHAR (SMB_TYP_REG, protocol);
|
|
SMBDELAY;
|
|
|
|
// Set SMBus Device Command value
|
|
if (SmbClass->CurrentSmb->Protocol >= SMB_WRITE_BYTE) {
|
|
SmbPrint (SMB_IO, ("SmbAliInitTransaction: IO write SMB_CMD = 0x%02x \n", SmbClass->CurrentSmb->Command));
|
|
WRITE_PORT_UCHAR (SMB_CMD_REG, SmbClass->CurrentSmb->Command);
|
|
SMBDELAY;
|
|
}
|
|
|
|
switch (SmbClass->CurrentSmb->Protocol) {
|
|
case SMB_WRITE_WORD:
|
|
case SMB_PROCESS_CALL:
|
|
|
|
// Set Data
|
|
SmbPrint (SMB_IO, ("SmbAliInitTransaction: IO write DEV_DATA1 = 0x%02x \n", SmbClass->CurrentSmb->Data[1]));
|
|
WRITE_PORT_UCHAR (DEV_DATA1_REG, SmbClass->CurrentSmb->Data[1]);
|
|
SMBDELAY;
|
|
|
|
// Fall through to set low byte of word
|
|
case SMB_SEND_BYTE:
|
|
case SMB_WRITE_BYTE:
|
|
|
|
// Set Data
|
|
SmbPrint (SMB_IO, ("SmbAliInitTransaction: IO write DEV_DATA0 = 0x%02x \n", SmbClass->CurrentSmb->Data[0]));
|
|
WRITE_PORT_UCHAR (DEV_DATA0_REG, SmbClass->CurrentSmb->Data[0]);
|
|
SMBDELAY;
|
|
|
|
break;
|
|
case SMB_WRITE_BLOCK:
|
|
// BUGBUG: not yet implemented.
|
|
SmbPrint (SMB_ERROR, ("SmbAliInitTransaction: Write Block not implemented. press 'g' to write random data.\n"));
|
|
DbgBreakPoint();
|
|
break;
|
|
}
|
|
|
|
// Initiate Transaction
|
|
SmbPrint (SMB_IO, ("SmbAliInitTransaction: IO write STR_PORT = 0x%02x \n", STR_PORT_START));
|
|
WRITE_PORT_UCHAR (STR_PORT_REG, STR_PORT_START);
|
|
|
|
AliData->CompleteTimeOut = SmbIoCompleteTimeOut;
|
|
|
|
KeSetTimer (&AliData->CompleteTimer,
|
|
SmbIoPollRate,
|
|
&AliData->CompleteDpc);
|
|
|
|
|
|
}
|
|
|
|
VOID
|
|
SmbAliCompleteTransactionDpc (
|
|
IN struct _KDPC *Dpc,
|
|
IN struct _SMB_CLASS* SmbClass,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
{
|
|
PSMB_ALI_DATA AliData = (PSMB_ALI_DATA)(SmbClass->Miniport);
|
|
|
|
IoQueueWorkItem (AliData->CompleteWorker,
|
|
SmbAliCompleteTransactionWorker,
|
|
DelayedWorkQueue,
|
|
SmbClass);
|
|
}
|
|
|
|
VOID
|
|
SmbAliCompleteTransactionWorker (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN struct _SMB_CLASS* SmbClass
|
|
)
|
|
{
|
|
PSMB_ALI_DATA AliData = (PSMB_ALI_DATA)(SmbClass->Miniport);
|
|
UCHAR i, smb_sts;
|
|
UCHAR smbStatus;
|
|
|
|
SmbPrint (SMB_TRACE, ("SmbAliCompleteTransactionWorker: Entered \n"));
|
|
|
|
smbStatus = SMB_STATUS_OK;
|
|
|
|
if (!SmbAliTransactionComplete(AliData, &smbStatus)) {
|
|
//
|
|
// Timeout
|
|
//
|
|
|
|
if (AliData->CompleteTimeOut == 0) {
|
|
SmbPrint (SMB_TRACE, ("SmbAliCompleteTransactionWorker: Transation timed out. Resetting host. \n"));
|
|
SmbAliResetHost (AliData);
|
|
|
|
smbStatus = SMB_TIMEOUT;
|
|
|
|
} else {
|
|
SmbPrint (SMB_TRACE, ("SmbAliCompleteTransactionWorker: Not complete. Waiting (%d)... \n", AliData->CompleteTimeOut));
|
|
AliData->CompleteTimeOut--;
|
|
KeSetTimer (&AliData->CompleteTimer,
|
|
SmbIoPollRate,
|
|
&AliData->CompleteDpc);
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
if (smbStatus == SMB_STATUS_OK) {
|
|
//
|
|
// If transaction was successful, read data.
|
|
//
|
|
|
|
switch (SmbClass->CurrentSmb->Protocol) {
|
|
case SMB_READ_WORD:
|
|
case SMB_PROCESS_CALL:
|
|
|
|
// Read High byte
|
|
SmbClass->CurrentSmb->Data[1] = READ_PORT_UCHAR (DEV_DATA1_REG);
|
|
SMBDELAY;
|
|
SmbPrint (SMB_IO, ("SmbAliCompleteTransactionWorker: IO read DEV_DATA1 = 0x%02x \n", SmbClass->CurrentSmb->Data[1]));
|
|
|
|
// Fall through to set low byte of word
|
|
case SMB_RECEIVE_BYTE:
|
|
case SMB_READ_BYTE:
|
|
|
|
// Read Low Byte
|
|
SmbClass->CurrentSmb->Data[0] = READ_PORT_UCHAR (DEV_DATA0_REG);
|
|
SMBDELAY;
|
|
SmbPrint (SMB_IO, ("SmbAliCompleteTransactionWorker: IO read DEV_DATA0 = 0x%02x \n", SmbClass->CurrentSmb->Data[0]));
|
|
break;
|
|
case SMB_READ_BLOCK:
|
|
// Read Block Count
|
|
SmbClass->CurrentSmb->BlockLength = READ_PORT_UCHAR (DEV_DATA0_REG);
|
|
SMBDELAY;
|
|
SmbPrint (SMB_IO, ("SmbAliCompleteTransactionWorker: IO read DEV_DATA0 (block length)= 0x%02x \n", SmbClass->CurrentSmb->BlockLength));
|
|
if (SmbClass->CurrentSmb->BlockLength >= 32) {
|
|
DbgBreakPoint();
|
|
SmbClass->CurrentSmb->BlockLength = 0;
|
|
}
|
|
|
|
// Reset Data pointer
|
|
// smb_sts = READ_PORT_UCHAR (SMB_STS_REG);
|
|
// SMBDELAY;
|
|
// SmbPrint (SMB_IO, ("SmbAliCompleteTransaction: IO read SMB_STS = 0x%02x \n", smb_sts));
|
|
smb_sts = SMB_STS_SMB_IDX_CLR;
|
|
SmbPrint (SMB_IO, ("SmbAliCompleteTransactionWorker: IO write SMB_STS = 0x%02x \n", smb_sts));
|
|
WRITE_PORT_UCHAR (SMB_STS_REG, smb_sts);
|
|
SMBDELAY;
|
|
|
|
// Read data
|
|
for (i = 0; i < SmbClass->CurrentSmb->BlockLength; i++) {
|
|
SmbClass->CurrentSmb->Data[i] = READ_PORT_UCHAR (BLK_DATA_REG);
|
|
SMBDELAY;
|
|
SmbPrint (SMB_IO, ("SmbAliCompleteTransactionWorker: IO read BLK_DATA_REG (i = %d) = 0x%02x \n", i, SmbClass->CurrentSmb->Data[i]));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else // smbStatus != SMB_STATUS_OK
|
|
{
|
|
//
|
|
// Retry the transaction up to 5 times before returning to the caller.
|
|
// REVIEW: Only do this for certain devices, commands, or error status results?
|
|
//
|
|
|
|
if (AliData->InternalRetries < 5)
|
|
{
|
|
//SmbPrint (SMB_IO_RESULT, (" SMBus Transaction status: %02x, retrying...\n", smbStatus));
|
|
AliData->InternalRetries += 1;
|
|
|
|
// Send the work item back to the init worker
|
|
AliData->InitTimeOut = SmbIoInitTimeOut;
|
|
KeSetTimer (&AliData->InitTimer,
|
|
SmbIoPollRate,
|
|
&AliData->InitDpc);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Clear any previous status.
|
|
//SmbPrint (SMB_IO, ("SmbAliCompleteTransaction: IO write SMB_STS = 0x%02x \n", SMB_STS_CLEAR));
|
|
//WRITE_PORT_UCHAR (SMB_STS_REG, SMB_STS_CLEAR);
|
|
// SMBDELAY;
|
|
|
|
SmbClass->CurrentSmb->Status = smbStatus;
|
|
SmbPrint (SMB_IO, ("SmbAliCompleteTransactionWorker: SMB Status = 0x%x\n", smbStatus));
|
|
SmbClass->CurrentIrp->IoStatus.Status = STATUS_SUCCESS;
|
|
SmbClass->CurrentIrp->IoStatus.Information = sizeof(SMB_REQUEST);
|
|
|
|
SmbPrint (SMB_IO_RESULT, (" Prtcl = %02x Addr = %02x Cmd = %02x BL = %02x Data[0,1] = %02x %02x Sts = %02x Rty = %02x\n",
|
|
SmbClass->CurrentSmb->Protocol,
|
|
SmbClass->CurrentSmb->Address,
|
|
SmbClass->CurrentSmb->Command,
|
|
SmbClass->CurrentSmb->BlockLength,
|
|
SmbClass->CurrentSmb->Data[0],
|
|
(SMB_READ_WORD == SmbClass->CurrentSmb->Protocol ||
|
|
SMB_WRITE_WORD == SmbClass->CurrentSmb->Protocol ||
|
|
(SMB_READ_BLOCK == SmbClass->CurrentSmb->Protocol &&
|
|
SmbClass->CurrentSmb->BlockLength >= 2)) ?
|
|
SmbClass->CurrentSmb->Data[1] : 0xFF,
|
|
SmbClass->CurrentSmb->Status,
|
|
AliData->InternalRetries));
|
|
|
|
SmbClassLockDevice (SmbClass);
|
|
SmbClassCompleteRequest (SmbClass);
|
|
SmbClassUnlockDevice (SmbClass);
|
|
|
|
#if DBG
|
|
//
|
|
// Track the # of successful transactions, and if not successful,
|
|
// the types of errors encountered.
|
|
//
|
|
if (SMB_STATUS_OK == smbStatus)
|
|
DbgSuccess += 1;
|
|
else
|
|
{
|
|
DbgFailure += 1;
|
|
if (SMB_TIMEOUT == smbStatus)
|
|
DbgTimeOut += 1;
|
|
else if (SMB_ADDRESS_NOT_ACKNOWLEDGED == smbStatus)
|
|
DbgAddrNotAck += 1;
|
|
else
|
|
DbgOtherErr += 1;
|
|
}
|
|
|
|
if ((DbgSuccess + DbgFailure) % 100 == 0)
|
|
SmbPrint(SMB_STATS, ("SmbAliCompleteTransactionWorker: Stats:\n"
|
|
" Success: %d, Failure: %d, %%: %d\n"
|
|
" TimeOut: %d, AddrNotAck: %d, Other: %d\n",
|
|
DbgSuccess, DbgFailure, DbgSuccess * 100 / (DbgSuccess + DbgFailure),
|
|
DbgTimeOut, DbgAddrNotAck, DbgOtherErr));
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
SmbAliNotifyHandler (
|
|
IN PVOID Context,
|
|
IN ULONG NotifyValue
|
|
)
|
|
{
|
|
PSMB_ALI_DATA AliData = (PSMB_ALI_DATA) Context;
|
|
ULONG address;
|
|
ULONG data;
|
|
UCHAR smb_sts;
|
|
|
|
SmbPrint (SMB_TRACE, ("SmbAliNotifyHandler: Entered"));
|
|
|
|
smb_sts = READ_PORT_UCHAR (SMB_STS_REG);
|
|
SMBDELAY;
|
|
SmbPrint (SMB_TRACE, ("SmbAliNotifyHandler: SMB_STS = %02x", smb_sts));
|
|
|
|
if (smb_sts & (SMB_STS_ALERT_STS || SMB_STS_SCI_I_STS)) {
|
|
//
|
|
// Alert Reponse
|
|
//
|
|
|
|
} else if (smb_sts & SMB_STS_SCI_I_STS) {
|
|
//
|
|
// Last Transaction completed
|
|
//
|
|
|
|
} else {
|
|
//
|
|
// Check for errors, etc.
|
|
//
|
|
}
|
|
|
|
IoQueueWorkItem (AliData->WorkItem,
|
|
SmbAliWorkerThread,
|
|
DelayedWorkQueue,
|
|
AliData);
|
|
|
|
}
|
|
|
|
VOID
|
|
SmbAliWorkerThread (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PSMB_ALI_DATA AliData = (PSMB_ALI_DATA) Context;
|
|
|
|
SmbPrint (SMB_TRACE, ("SmbAliIrpCompletionWorker: Entered"));
|
|
//
|
|
// Complete Irps here
|
|
//
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SmbAliSyncronousIrpCompletion (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when the lower driver completes an IRP.
|
|
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object for the device.
|
|
|
|
Irp - Irp completed.
|
|
|
|
Context - Driver defined context.
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status from the operation.
|
|
|
|
--*/
|
|
{
|
|
PKEVENT event = Context;
|
|
|
|
|
|
KeSetEvent(event,
|
|
1,
|
|
FALSE);
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
SmbAliTransactionComplete (
|
|
PSMB_ALI_DATA AliData,
|
|
PUCHAR SmbStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks to see if there is the last transation was completed.
|
|
|
|
Arguments:
|
|
|
|
AliData - minidriver device extension.
|
|
|
|
SmbStatus - Status being returned.
|
|
|
|
Return Value:
|
|
|
|
True if transaction completed or had an error.
|
|
False if it is still waiting.
|
|
|
|
--*/
|
|
|
|
{
|
|
UCHAR smb_sts;
|
|
|
|
smb_sts = READ_PORT_UCHAR (SMB_STS_REG);
|
|
SMBDELAY;
|
|
SmbPrint (SMB_IO, ("SmbAliTransactionComplete: IO read SMB_STS = 0x%02x \n", smb_sts));
|
|
if (smb_sts & SMB_STS_HOST_BSY) {
|
|
SmbPrint (SMB_IO, ("SmbAliTransactionComplete: Transaction Not Complete: HOST BUSY\n"));
|
|
return FALSE;
|
|
}
|
|
if (!(smb_sts & SMB_STS_IDLE_STS)) {
|
|
SmbPrint (SMB_IO, ("SmbAliTransactionComplete: Transaction Not Complete: Idle Not Indicated\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
if (smb_sts & SMB_STS_SCI_I_STS) {
|
|
//
|
|
// Transaction is complete
|
|
//
|
|
*SmbStatus = SMB_STATUS_OK;
|
|
return TRUE;
|
|
}
|
|
if (smb_sts & SMB_STS_FAILED) {
|
|
*SmbStatus = SMB_UNKNOWN_FAILURE;
|
|
} else if (smb_sts & SMB_STS_BUS_ERR) {
|
|
*SmbStatus = SMB_ADDRESS_NOT_ACKNOWLEDGED;
|
|
} else if (smb_sts & SMB_STS_DRV_ERR) {
|
|
*SmbStatus = SMB_TIMEOUT;
|
|
} else {
|
|
//
|
|
// This state really shouldn't be reached.
|
|
// Reset the SMBus host
|
|
//
|
|
SmbPrint (SMB_BUS_ERROR, ("SmbAliTransactionComplete: Invalid SMBus host state.\n"));
|
|
|
|
*SmbStatus = SMB_UNKNOWN_ERROR;
|
|
}
|
|
//
|
|
// For the three know error tpes we want to reset the bus
|
|
//
|
|
SmbPrint (SMB_BUS_ERROR, ("SmbAliTransactionComplete: SMBus error: 0x%x \n", *SmbStatus));
|
|
|
|
// Don't reset the bus etc. if this is a Bus Collision error
|
|
if ( *SmbStatus == SMB_ADDRESS_NOT_ACKNOWLEDGED )
|
|
{
|
|
// Should we clear the bits, lets try it
|
|
SmbPrint (SMB_IO, ("SmbAliCompleteTransaction: Clearing Error Bits. IO write SMB_STS = 0x%02x \n", SMB_STS_CLEAR));
|
|
WRITE_PORT_UCHAR (SMB_STS_REG, SMB_STS_CLEAR);
|
|
SMBDELAY;
|
|
}
|
|
else
|
|
{
|
|
SmbAliResetHost (AliData);
|
|
SmbAliResetBus (AliData);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
SmbAliHostBusy (
|
|
PSMB_ALI_DATA AliData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks to see if the Host controller is free to start a
|
|
new transaction.
|
|
|
|
Arguments:
|
|
|
|
AliData - minidriver device extension.
|
|
|
|
Return Value:
|
|
|
|
True if The host is busy.
|
|
False if it free for use.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
UCHAR smb_sts;
|
|
|
|
SmbPrint (SMB_TRACE, ("SmbAliHostBusy: Entered \n"));
|
|
|
|
smb_sts = READ_PORT_UCHAR (SMB_STS_REG);
|
|
SMBDELAY;
|
|
SmbPrint (SMB_IO, ("SmbAliHostBusy: IO read SMB_STS = 0x%02x \n", smb_sts));
|
|
|
|
if (smb_sts & SMB_STS_ALERT_STS) {
|
|
SmbPrint (SMB_TRACE, ("SmbAliHostBusy: Alert Detected \n"));
|
|
DbgBreakPoint();
|
|
SmbAliHandleAlert (AliData);
|
|
|
|
//
|
|
// Say device is still busy for now. BUGBUG
|
|
return TRUE;
|
|
}
|
|
|
|
if ( smb_sts == SMB_STS_LAST_CMD_COMPLETED )
|
|
{
|
|
//
|
|
// Clear the done bit
|
|
SmbPrint (SMB_IO, ("SmbAliHostBusy: IO write SMB_TYP = 0x%02x \n", SMB_STS_CLEAR_DONE));
|
|
WRITE_PORT_UCHAR (SMB_STS_REG, SMB_STS_CLEAR_DONE);
|
|
SMBDELAY;
|
|
return FALSE;
|
|
}
|
|
|
|
if ( smb_sts == SMB_STS_IDLE_STS )
|
|
{
|
|
//
|
|
// No bits are set, Host is not busy
|
|
//
|
|
SmbPrint (SMB_TRACE, ("SmbAliHostBusy: Not busy \n"));
|
|
return FALSE;
|
|
}
|
|
|
|
if ( smb_sts & SMB_STS_ERRORS ) {
|
|
//
|
|
// Clear it.
|
|
// Wait a cycle before continuing.
|
|
//
|
|
SmbPrint (SMB_IO, ("SmbAliHostBusy: IO write SMB_TYP = 0x%02x \n", SMB_STS_CLEAR));
|
|
WRITE_PORT_UCHAR (SMB_STS_REG, SMB_STS_CLEAR);
|
|
SMBDELAY;
|
|
return TRUE;
|
|
}
|
|
|
|
if ((smb_sts & SMB_STS_HOST_BSY) || !(smb_sts & SMB_STS_IDLE_STS)) {
|
|
//
|
|
// Host is busy
|
|
//
|
|
|
|
SmbPrint (SMB_TRACE, ("SmbAliHostBusy: Host Busy \n"));
|
|
return TRUE;
|
|
}
|
|
|
|
SmbPrint (SMB_ERROR, ("SmbAliHostBusy: Exiting (Why?) \n"));
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
SmbAliHandleAlert (
|
|
PSMB_ALI_DATA AliData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reads the alert data and sends notification to SMB class.
|
|
|
|
Arguments:
|
|
|
|
AliData - minidriver device extension.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//BUGBUG not yet implemented
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
SmbAliResetBus (
|
|
PSMB_ALI_DATA AliData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This resets the bus by sending the timeout command.
|
|
|
|
Arguments:
|
|
|
|
AliData - minidriver device extension.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
UCHAR smb_sts;
|
|
|
|
smb_sts = SMB_TYP_T_OUT_CMD;
|
|
SmbPrint (SMB_IO, ("SmbAliResetBus: IO write SMB_TYP = 0x%02x \n", smb_sts));
|
|
WRITE_PORT_UCHAR (SMB_TYP_REG, smb_sts);
|
|
SMBDELAY;
|
|
|
|
SmbPrint (SMB_IO, ("SmbAliResetBus: IO write SMB_STS = 0x%02x \n", SMB_STS_CLEAR));
|
|
WRITE_PORT_UCHAR (SMB_STS_REG, SMB_STS_CLEAR);
|
|
SMBDELAY;
|
|
}
|
|
|
|
VOID
|
|
SmbAliResetHost (
|
|
PSMB_ALI_DATA AliData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This resets the host by sending the kill command.
|
|
|
|
Arguments:
|
|
|
|
AliData - minidriver device extension.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
UCHAR smb_sts;
|
|
UCHAR timeout = 5;
|
|
|
|
smb_sts = SMB_TYP_KILL;
|
|
SmbPrint (SMB_IO, ("SmbAliResetHost: IO write SMB_TYP = 0x%02x \n", smb_sts));
|
|
WRITE_PORT_UCHAR (SMB_TYP_REG, smb_sts);
|
|
SMBDELAY;
|
|
|
|
SmbPrint (SMB_IO, ("SmbAliResetHost: IO write SMB_STS = 0x%02x \n", SMB_STS_CLEAR));
|
|
WRITE_PORT_UCHAR (SMB_STS_REG, SMB_STS_CLEAR);
|
|
SMBDELAY;
|
|
|
|
do {
|
|
KeDelayExecutionThread (KernelMode, FALSE, &SmbIoPollRate);
|
|
smb_sts = READ_PORT_UCHAR (SMB_STS_REG);
|
|
SmbPrint (SMB_IO, ("SmbAliResetHost: IO read SMB_STS = 0x%02x \n", smb_sts));
|
|
|
|
if (! (timeout--)) {
|
|
break;
|
|
}
|
|
} while (smb_sts & SMB_STS_FAILED);
|
|
}
|
|
|
|
#ifdef USE_IO_DELAY
|
|
|
|
LARGE_INTEGER DbgDelay = {-1,-1};
|
|
VOID SmbDelay(VOID)
|
|
{
|
|
KeDelayExecutionThread (KernelMode, FALSE, &DbgDelay);
|
|
}
|
|
|
|
#endif
|