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