windows-nt/Source/XPSP1/NT/drivers/wdm/input/tabletpc/smblite/ioctl.c
2020-09-26 16:20:57 +08:00

540 lines
17 KiB
C

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
ioctl.c
Abstract: Contains routines to support ioctl queries for the SMB Back Light
device.
Environment:
Kernel mode
Author:
Michael Tsang (MikeTs) 20-Nov-2000
Revision History:
--*/
#include "pch.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, SmbLiteIoctl)
#pragma alloc_text(PAGE, GetBackLightBrightness)
#pragma alloc_text(PAGE, RegQueryDeviceParam)
#endif
/*++
@doc EXTERNAL
@func NTSTATUS | SmbLiteIoctl |
Process the Device Control IRPs sent to this device.
@parm IN PDRIVER_OBJECT | DevObj | Points to the driver object.
@parm IN PIRP | Irp | Points to an I/O Request Packet.
@rvalue SUCCESS | returns STATUS_SUCCESS
@rvalue FAILURE | returns NT status code
--*/
NTSTATUS EXTERNAL
SmbLiteIoctl(
IN PDEVICE_OBJECT DevObj,
IN PIRP Irp
)
{
PROCNAME("SmbLiteIoctl")
NTSTATUS status;
PIO_STACK_LOCATION irpsp;
PSMBLITE_DEVEXT devext;
PAGED_CODE();
irpsp = IoGetCurrentIrpStackLocation(Irp);
ENTER(1, ("(DevObj=%p,Irp=%p,IrpSp=%p,Ioctl=%s)\n",
DevObj, Irp, irpsp,
LookupName(irpsp->Parameters.DeviceIoControl.IoControlCode,
IoctlNames)));
devext = DevObj->DeviceExtension;
status = IoAcquireRemoveLock(&devext->RemoveLock, Irp);
if (NT_SUCCESS(status))
{
BOOLEAN fNeedCompletion = TRUE;
ASSERT(devext->dwfSmbLite & SMBLITEF_DEVICE_STARTED);
Irp->IoStatus.Information = 0;
switch(irpsp->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_SMBLITE_GETBRIGHTNESS:
if (irpsp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(SMBLITE_BRIGHTNESS))
{
status = STATUS_BUFFER_TOO_SMALL;
WARNPRINT(("GetBrightness buffer too small (len=%d,required=%d).\n",
irpsp->Parameters.DeviceIoControl.OutputBufferLength,
sizeof(SMBLITE_BRIGHTNESS)));
}
else
{
PSMBLITE_BRIGHTNESS Brightness = Irp->UserBuffer;
try
{
ProbeForWrite(Brightness,
sizeof(*Brightness),
sizeof(UCHAR));
}
except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
WARNPRINT(("Invalid GetBrightness buffer (status=%x,Buff=%p).\n",
status, Brightness));
}
if (NT_SUCCESS(status))
{
status = GetBackLightBrightness(devext, Brightness);
if (NT_SUCCESS(status))
{
Irp->IoStatus.Information = sizeof(*Brightness);
}
}
}
break;
case IOCTL_SMBLITE_SETBRIGHTNESS:
if (irpsp->Parameters.DeviceIoControl.InputBufferLength !=
sizeof(SMBLITE_SETBRIGHTNESS))
{
status = STATUS_INFO_LENGTH_MISMATCH;
WARNPRINT(("SetBrightness buffer length mismatch (len=%d,required=%d).\n",
irpsp->Parameters.DeviceIoControl.InputBufferLength,
sizeof(SMBLITE_SETBRIGHTNESS)));
}
else
{
PSMBLITE_SETBRIGHTNESS SetBrightness = (PSMBLITE_SETBRIGHTNESS)
irpsp->Parameters.DeviceIoControl.Type3InputBuffer;
try
{
ProbeForRead(SetBrightness,
sizeof(*SetBrightness),
sizeof(UCHAR));
}
except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
WARNPRINT(("Invalid SetBrightness buffer (status=%x,Buff=%p).\n",
status, SetBrightness));
}
if (NT_SUCCESS(status))
{
status = SetBackLightBrightness(
devext,
&SetBrightness->Brightness,
SetBrightness->fSaveSettings);
}
}
break;
#ifdef SYSACC
case IOCTL_SYSACC_MEM_REQUEST:
status = STATUS_NOT_SUPPORTED;
break;
case IOCTL_SYSACC_IO_REQUEST:
status = STATUS_NOT_SUPPORTED;
break;
case IOCTL_SYSACC_PCICFG_REQUEST:
status = STATUS_NOT_SUPPORTED;
break;
case IOCTL_SYSACC_SMBUS_REQUEST:
if ((irpsp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(SMB_REQUEST)) ||
(irpsp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(SMB_REQUEST)))
{
status = STATUS_BUFFER_TOO_SMALL;
WARNPRINT(("SMBusRequest buffer too small (len=%d,required=%d).\n",
irpsp->Parameters.DeviceIoControl.InputBufferLength,
sizeof(SMB_REQUEST)));
}
else
{
PSMB_REQUEST SmbReqIn = (PSMB_REQUEST)
Irp->AssociatedIrp.SystemBuffer;
PSMB_REQUEST SmbReqOut = (PSMB_REQUEST)
Irp->UserBuffer;
try
{
ProbeForWrite(SmbReqOut,
sizeof(*SmbReqOut),
sizeof(UCHAR));
}
except (EXCEPTION_EXECUTE_HANDLER)
{
status = GetExceptionCode();
WARNPRINT(("Invalid SMBRequest buffer (status=%x,Buff=%p).\n",
status, SmbReqOut));
}
if (NT_SUCCESS(status))
{
status = SMBRequest(devext, SmbReqIn);
if (NT_SUCCESS(status))
{
RtlCopyMemory(SmbReqOut,
SmbReqIn,
sizeof(*SmbReqOut));
Irp->IoStatus.Information = sizeof(*SmbReqOut);
}
}
}
break;
#endif
default:
WARNPRINT(("unsupported ioctl code (ioctl=%s)\n",
LookupName(irpsp->Parameters.DeviceIoControl.IoControlCode,
IoctlNames)));
status = STATUS_NOT_SUPPORTED;
break;
}
IoReleaseRemoveLock(&devext->RemoveLock, Irp);
}
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
EXIT(1, ("=%x\n", status));
return status;
} //SmbLiteIoctl
/*++
@doc INTERNAL
@func NTSTATUS | GetBackLightBrightness |
Query the Backlight brightness via the SMBus driver.
@parm IN PSMBLITE_DEVEXT | devext | Points to the device extension.
@parm OUT PSMBLITE_BRIGHTNESS | Brightness | To hold the brightness value.
@rvalue Always returns STATUS_SUCCESS
--*/
NTSTATUS INTERNAL
GetBackLightBrightness(
IN PSMBLITE_DEVEXT devext,
OUT PSMBLITE_BRIGHTNESS Brightness
)
{
PROCNAME("GetBackLightBrightness")
PAGED_CODE();
ENTER(2, ("(devext=%p,Brightness=%p)\n", devext, Brightness));
*Brightness = devext->BackLightBrightness;
EXIT(2, ("=%x (ACValue=%d,DCValue=%d)\n",
STATUS_SUCCESS, Brightness->bACValue, Brightness->bDCValue));
return STATUS_SUCCESS;
} //GetBackLightBrightness
/*++
@doc INTERNAL
@func NTSTATUS | SetBackLightBrightness |
Set the Backlight brightness via the SMBus driver.
@parm IN PSMBLITE_DEVEXT | devext | Points to the device extension.
@parm IN PSMBLITE_BRIGHTNESS | Brightness | The backlight brightness
values.
@parm IN BOOL | fSaveSettings | TRUE if need to save setting in the
registry.
@rvalue SUCCESS | returns STATUS_SUCCESS
@rvalue FAILURE | returns NT status code
--*/
NTSTATUS INTERNAL
SetBackLightBrightness(
IN PSMBLITE_DEVEXT devext,
IN PSMBLITE_BRIGHTNESS Brightness,
IN BOOLEAN fSaveSettings
)
{
PROCNAME("SetBackLightBrightness")
NTSTATUS status;
SMB_REQUEST SmbReq;
UCHAR bBrightness;
ENTER(2, ("(devext=%p,Brightness=%p,fSave=%x,ACValue=%d,DCValue=%d)\n",
devext, Brightness, fSaveSettings, Brightness->bACValue,
Brightness->bDCValue));
//
// Note: this routine must not be pageable because it could be called
// by PowerStateCallbackProc which could be called at DPC.
//
bBrightness = (devext->dwfSmbLite & SMBLITEF_SYSTEM_ON_AC)?
Brightness->bACValue: Brightness->bDCValue;
DBGPRINT(1, ("Set Brightness level=%d (%s).\n",
bBrightness,
(devext->dwfSmbLite & SMBLITEF_SYSTEM_ON_AC)? "AC": "DC"));
SmbReq.Protocol = SMB_WRITE_BYTE;
SmbReq.Address = SMBADDR_BACKLIGHT;
SmbReq.Command = SMBCMD_BACKLIGHT_NORMAL;
SmbReq.Data[0] = (UCHAR)(bBrightness << 2);
status = SMBRequest(devext, &SmbReq);
if (NT_SUCCESS(status))
{
devext->BackLightBrightness = *Brightness;
if (fSaveSettings)
{
RegSetDeviceParam(devext->PDO,
gcwstrACBrightness,
&Brightness->bACValue,
sizeof(Brightness->bACValue));
RegSetDeviceParam(devext->PDO,
gcwstrDCBrightness,
&Brightness->bDCValue,
sizeof(Brightness->bDCValue));
}
}
EXIT(2, ("=%x\n", status));
return status;
} //SetBackLightBrightness
/*++
@doc INTERNAL
@func NTSTATUS | SMBRequest |
Make a request to the SMBus driver.
@parm IN PSMBLITE_DEVEXT | devext | Points to the device extension.
@parm IN OUT PSMB_REQUEST | SmbReq | Points to the SMB request.
@rvalue SUCCESS | returns STATUS_SUCCESS
@rvalue FAILURE | returns NT status code
--*/
NTSTATUS INTERNAL
SMBRequest(
IN PSMBLITE_DEVEXT devext,
IN OUT PSMB_REQUEST SmbReq
)
{
PROCNAME("SMBRequest")
NTSTATUS status;
PIRP irp;
KEVENT Event;
IO_STATUS_BLOCK iosb;
ENTER(2, ("(devext=%p,Req=%p,Protocol=%s,Addr=%x,Cmd=%x,Data0=%x,Data1=%x)\n",
devext, SmbReq, LookupName(SmbReq->Protocol, ProtocolNames),
SmbReq->Address, SmbReq->Command, SmbReq->Data[0],
SmbReq->Data[1]));
//
// Note: this routine must not be pageable because it could be called
// by SetBackLightBrightness and then PowerStateCallbackProc which could
// be called at DPC.
//
KeInitializeEvent(&Event, NotificationEvent, FALSE);
irp = IoBuildDeviceIoControlRequest(SMB_BUS_REQUEST,
devext->LowerDevice,
SmbReq,
sizeof(SMB_REQUEST),
NULL,
0,
TRUE,
&Event,
&iosb);
if (irp != NULL)
{
status = IoCallDriver(devext->LowerDevice, irp);
if (status == STATUS_PENDING)
{
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
status = iosb.Status;
}
if (!NT_SUCCESS(status))
{
ERRPRINT(("failed SMB request ioctl (status=%x).\n", status));
}
}
else
{
ERRPRINT(("failed to build smb request ioctl request.\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
}
EXIT(2, ("=%x\n", status));
return status;
} //SMBRequest
/*++
@doc INTERNAL
@func NTSTATUS | RegQueryDeviceParam | Query the registry for a device
parameter.
@parm IN PDEVICE_OBJECT | DevObj | Points to the device object.
@parm IN PCWSTR | pwstrParamName | Points to the param name string.
@parm OUT PVOID | pbBuff | Points to the buffer to hold the result.
@parm IN ULONG | dwcbLen | Specifies the length of the buffer.
@rvalue SUCCESS | Returns STATUS_SUCCESS
@rvalue FAILURE | Returns NT status code
--*/
NTSTATUS INTERNAL
RegQueryDeviceParam(
IN PDEVICE_OBJECT DevObj,
IN PCWSTR pwstrParamName,
OUT PVOID pbBuff,
IN ULONG dwcbLen
)
{
PROCNAME("RegQueryDeviceParam")
NTSTATUS status;
ULONG dwSize;
PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
PAGED_CODE();
ENTER(2, ("(DevObj=%p,ParamName=%S,pbBuff=%p,Len=%d)\n",
DevObj, pwstrParamName, pbBuff, dwcbLen));
dwSize = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + dwcbLen;
ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePoolWithTag(
NonPagedPool,
dwSize,
SMBLITE_POOLTAG);
if (ValueInfo != NULL)
{
HANDLE hkey;
status = IoOpenDeviceRegistryKey(DevObj,
PLUGPLAY_REGKEY_DEVICE,
STANDARD_RIGHTS_READ,
&hkey);
if (NT_SUCCESS(status))
{
UNICODE_STRING ucKeyName;
RtlInitUnicodeString(&ucKeyName, pwstrParamName);
status = ZwQueryValueKey(hkey,
&ucKeyName,
KeyValuePartialInformation,
ValueInfo,
dwSize,
&dwSize);
if (NT_SUCCESS(status))
{
ASSERT(ValueInfo->DataLength == dwcbLen);
RtlCopyMemory(pbBuff, ValueInfo->Data, dwcbLen);
}
else
{
WARNPRINT(("failed to read parameter %S (status=%x)\n",
pwstrParamName, status));
}
ZwClose(hkey);
}
else
{
ERRPRINT(("failed to open device registry key (status=%x)\n",
status));
}
ExFreePool(ValueInfo);
}
else
{
status = STATUS_INSUFFICIENT_RESOURCES;
ERRPRINT(("failed to allocate registry value buffer (size=%d)\n",
dwSize));
}
EXIT(2, ("=%x\n", status));
return status;
} //RegQueryDeviceParam
/*++
@doc INTERNAL
@func NTSTATUS | RegSetDeviceParam | Set a device parameter into the
registry.
@parm IN PDEVICE_OBJECT | DevObj | Points to the device object.
@parm IN PCWSTR | pwstrParamName | Points to the param name string.
@parm IN PVOID | pbBuff | Points to the buffer containing data.
@parm IN ULONG | dwcbLen | Specifies the length of the buffer.
@rvalue SUCCESS | Returns STATUS_SUCCESS
@rvalue FAILURE | Returns NT status code
--*/
NTSTATUS INTERNAL
RegSetDeviceParam(
IN PDEVICE_OBJECT DevObj,
IN PCWSTR pwstrParamName,
IN PVOID pbBuff,
IN ULONG dwcbLen
)
{
PROCNAME("RegSetDeviceParam")
NTSTATUS status;
HANDLE hkey;
ENTER(2, ("(DevObj=%p,ParamName=%S,pbBuff=%p,Len=%d)\n",
DevObj, pwstrParamName, pbBuff, dwcbLen));
//
// Note: this routine must not be pageable because it could be called
// by SetBackLightBrightness and then PowerStateCallbackProc which could
// be called at DPC.
//
status = IoOpenDeviceRegistryKey(DevObj,
PLUGPLAY_REGKEY_DEVICE,
STANDARD_RIGHTS_WRITE,
&hkey);
if (NT_SUCCESS(status))
{
UNICODE_STRING ucKeyName;
RtlInitUnicodeString(&ucKeyName, pwstrParamName);
status = ZwSetValueKey(hkey,
&ucKeyName,
0,
REG_BINARY,
pbBuff,
dwcbLen);
if (!NT_SUCCESS(status))
{
WARNPRINT(("failed to write device parameter %S (status=%x)\n",
pwstrParamName, status));
}
ZwClose(hkey);
}
else
{
ERRPRINT(("failed to open device registry key (status=%x)\n",
status));
}
EXIT(2, ("=%x\n", status));
return status;
} //RegSetDeviceParam