xref: /reactos/win32ss/drivers/miniport/vbe/vbemp.c (revision 9d3c3a75)
1 /*
2  * ReactOS VBE miniport video driver
3  * Copyright (C) 2004 Filip Navara
4  *
5  * Power Management and VBE 1.2 support
6  * Copyright (C) 2004 Magnus Olsen
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  * TODO:
23  * - Check input parameters everywhere.
24  * - Call VideoPortVerifyAccessRanges to reserve the memory we're about
25  *   to map.
26  */
27 
28 /* INCLUDES *******************************************************************/
29 
30 #include "vbemp.h"
31 
32 #include <devioctl.h>
33 
34 #undef LOWORD
35 #undef HIWORD
36 #define LOWORD(l)	((USHORT)((ULONG_PTR)(l)))
37 #define HIWORD(l)	((USHORT)(((ULONG_PTR)(l)>>16)&0xFFFF))
38 
39 VIDEO_ACCESS_RANGE VBEAccessRange[] =
40 {
41     { {{0x3b0}}, 0x3bb - 0x3b0 + 1, 1, 1, 0 },
42     { {{0x3c0}}, 0x3df - 0x3c0 + 1, 1, 1, 0 },
43     { {{0xa0000}}, 0x20000, 0, 1, 0 },
44 };
45 
46 /* PUBLIC AND PRIVATE FUNCTIONS ***********************************************/
47 
48 ULONG NTAPI
49 DriverEntry(IN PVOID Context1, IN PVOID Context2)
50 {
51    VIDEO_HW_INITIALIZATION_DATA InitData;
52 
53    VideoPortZeroMemory(&InitData, sizeof(InitData));
54    InitData.HwInitDataSize = sizeof(VIDEO_HW_INITIALIZATION_DATA);
55    InitData.HwFindAdapter = VBEFindAdapter;
56    InitData.HwInitialize = VBEInitialize;
57    InitData.HwStartIO = VBEStartIO;
58    InitData.HwResetHw = VBEResetHw;
59    InitData.HwGetPowerState = VBEGetPowerState;
60    InitData.HwSetPowerState = VBESetPowerState;
61    InitData.HwGetVideoChildDescriptor = VBEGetVideoChildDescriptor;
62    InitData.HwDeviceExtensionSize = sizeof(VBE_DEVICE_EXTENSION);
63    InitData.HwLegacyResourceList = VBEAccessRange;
64    InitData.HwLegacyResourceCount = ARRAYSIZE(VBEAccessRange);
65 
66    return VideoPortInitialize(Context1, Context2, &InitData, NULL);
67 }
68 
69 /*
70  * VBEFindAdapter
71  *
72  * Should detect a VBE compatible display adapter, but it's not possible
73  * to use video port Int 10 services at this time during initialization,
74  * so we always return NO_ERROR and do the real work in VBEInitialize.
75  */
76 
77 VP_STATUS NTAPI
78 VBEFindAdapter(
79    IN PVOID HwDeviceExtension,
80    IN PVOID HwContext,
81    IN PWSTR ArgumentString,
82    IN OUT PVIDEO_PORT_CONFIG_INFO ConfigInfo,
83    OUT PUCHAR Again)
84 {
85    if (VideoPortIsNoVesa())
86        return ERROR_DEV_NOT_EXIST;
87 
88    if (ConfigInfo->Length < sizeof(VIDEO_PORT_CONFIG_INFO))
89        return ERROR_INVALID_PARAMETER;
90 
91    ConfigInfo->VdmPhysicalVideoMemoryAddress = VBEAccessRange[2].RangeStart;
92    ConfigInfo->VdmPhysicalVideoMemoryLength = VBEAccessRange[2].RangeLength;
93    return NO_ERROR;
94 }
95 
96 /*
97  * VBESortModesCallback
98  *
99  * Helper function for sorting video mode list.
100  */
101 
102 static int
103 VBESortModesCallback(PVBE_MODEINFO VbeModeInfoA, PVBE_MODEINFO VbeModeInfoB)
104 {
105    /*
106     * FIXME: Until some reasonable method for changing video modes will
107     * be available we favor more bits per pixel. It should be changed
108     * later.
109     */
110    if (VbeModeInfoA->BitsPerPixel < VbeModeInfoB->BitsPerPixel) return -1;
111    if (VbeModeInfoA->BitsPerPixel > VbeModeInfoB->BitsPerPixel) return 1;
112    if (VbeModeInfoA->XResolution < VbeModeInfoB->XResolution) return -1;
113    if (VbeModeInfoA->XResolution > VbeModeInfoB->XResolution) return 1;
114    if (VbeModeInfoA->YResolution < VbeModeInfoB->YResolution) return -1;
115    if (VbeModeInfoA->YResolution > VbeModeInfoB->YResolution) return 1;
116    return 0;
117 }
118 
119 /*
120  * VBESortModes
121  *
122  * Simple function for sorting the video mode list. Uses bubble sort.
123  */
124 
125 VOID FASTCALL
126 VBESortModes(PVBE_DEVICE_EXTENSION DeviceExtension)
127 {
128    BOOLEAN Finished = FALSE;
129    ULONG Pos;
130    int Result;
131    VBE_MODEINFO TempModeInfo;
132    USHORT TempModeNumber;
133 
134    while (!Finished)
135    {
136       Finished = TRUE;
137       for (Pos = 0; Pos < DeviceExtension->ModeCount - 1; Pos++)
138       {
139          Result = VBESortModesCallback(
140             DeviceExtension->ModeInfo + Pos,
141             DeviceExtension->ModeInfo + Pos + 1);
142          if (Result > 0)
143          {
144             Finished = FALSE;
145 
146             VideoPortMoveMemory(
147                &TempModeInfo,
148                DeviceExtension->ModeInfo + Pos,
149                sizeof(VBE_MODEINFO));
150             TempModeNumber = DeviceExtension->ModeNumbers[Pos];
151 
152             VideoPortMoveMemory(
153                DeviceExtension->ModeInfo + Pos,
154                DeviceExtension->ModeInfo + Pos + 1,
155                sizeof(VBE_MODEINFO));
156             DeviceExtension->ModeNumbers[Pos] =
157                DeviceExtension->ModeNumbers[Pos + 1];
158 
159             VideoPortMoveMemory(
160                DeviceExtension->ModeInfo + Pos + 1,
161                &TempModeInfo,
162                sizeof(VBE_MODEINFO));
163             DeviceExtension->ModeNumbers[Pos + 1] = TempModeNumber;
164          }
165       }
166    }
167 }
168 
169 /*
170  * VBEInitialize
171  *
172  * Performs the first initialization of the adapter, after the HAL has given
173  * up control of the video hardware to the video port driver.
174  *
175  * This function performs these steps:
176  * - Gets global VBE information and finds if VBE BIOS is present.
177  * - Builds the internal mode list using the list of modes provided by
178  *   the VBE.
179  */
180 
181 BOOLEAN NTAPI
182 VBEInitialize(PVOID HwDeviceExtension)
183 {
184    INT10_BIOS_ARGUMENTS BiosRegisters;
185    VP_STATUS Status;
186    PVBE_DEVICE_EXTENSION VBEDeviceExtension =
187      (PVBE_DEVICE_EXTENSION)HwDeviceExtension;
188    ULONG Length;
189    ULONG ModeCount;
190    ULONG SuitableModeCount;
191    USHORT ModeTemp;
192    ULONG CurrentMode;
193    PVBE_MODEINFO VbeModeInfo;
194 
195    if (VideoPortIsNoVesa())
196    {
197       VBEDeviceExtension->Int10Interface.Version = 0;
198       VBEDeviceExtension->Int10Interface.Size = 0;
199       return FALSE;
200    }
201 
202    /*
203     * Get the Int 10 interface that we will use for allocating real
204     * mode memory and calling the video BIOS.
205     */
206 
207    VBEDeviceExtension->Int10Interface.Version = VIDEO_PORT_INT10_INTERFACE_VERSION_1;
208    VBEDeviceExtension->Int10Interface.Size = sizeof(VIDEO_PORT_INT10_INTERFACE);
209    Status = VideoPortQueryServices(
210       HwDeviceExtension,
211       VideoPortServicesInt10,
212       (PINTERFACE)&VBEDeviceExtension->Int10Interface);
213 
214    if (Status != NO_ERROR)
215    {
216       VideoPortDebugPrint(Error, "Failed to get Int 10 service functions (Status %x)\n", Status);
217       return FALSE;
218    }
219 
220    /*
221     * Allocate a bit of memory that will be later used for VBE transport
222     * buffer. This memory must be accessible from V86 mode so it must fit
223     * in the first megabyte of physical memory.
224     */
225 
226    Length = 0x400;
227    Status = VBEDeviceExtension->Int10Interface.Int10AllocateBuffer(
228       VBEDeviceExtension->Int10Interface.Context,
229       &VBEDeviceExtension->TrampolineMemorySegment,
230       &VBEDeviceExtension->TrampolineMemoryOffset,
231       &Length);
232 
233    if (Status != NO_ERROR)
234    {
235       VideoPortDebugPrint(Error, "Failed to allocate virtual memory (Status %x)\n", Status);
236       return FALSE;
237    }
238 
239    /*
240     * Get the VBE general information.
241     */
242 
243    VBEDeviceExtension->Int10Interface.Int10WriteMemory(
244       VBEDeviceExtension->Int10Interface.Context,
245       VBEDeviceExtension->TrampolineMemorySegment,
246       VBEDeviceExtension->TrampolineMemoryOffset,
247       "VBE2",
248       4);
249 
250    VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
251    BiosRegisters.Eax = VBE_GET_CONTROLLER_INFORMATION;
252    BiosRegisters.Edi = VBEDeviceExtension->TrampolineMemoryOffset;
253    BiosRegisters.SegEs = VBEDeviceExtension->TrampolineMemorySegment;
254    VBEDeviceExtension->Int10Interface.Int10CallBios(
255       VBEDeviceExtension->Int10Interface.Context,
256       &BiosRegisters);
257 
258    if (VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_SUCCESS)
259    {
260       VBEDeviceExtension->Int10Interface.Int10ReadMemory(
261          VBEDeviceExtension->Int10Interface.Context,
262          VBEDeviceExtension->TrampolineMemorySegment,
263          VBEDeviceExtension->TrampolineMemoryOffset,
264          &VBEDeviceExtension->VbeInfo,
265          sizeof(VBEDeviceExtension->VbeInfo));
266 
267       /* Verify the VBE signature. */
268       if (VideoPortCompareMemory(VBEDeviceExtension->VbeInfo.Signature, "VESA", 4) != 4)
269       {
270          VideoPortDebugPrint(Error, "No VBE BIOS present\n");
271          return FALSE;
272       }
273 
274       VideoPortDebugPrint(Trace, "VBE BIOS Present (%d.%d, %8ld Kb)\n",
275          VBEDeviceExtension->VbeInfo.Version / 0x100,
276          VBEDeviceExtension->VbeInfo.Version & 0xFF,
277          VBEDeviceExtension->VbeInfo.TotalMemory * 64);
278 
279 #ifdef VBE12_SUPPORT
280       if (VBEDeviceExtension->VbeInfo.Version < 0x102)
281 #else
282       if (VBEDeviceExtension->VbeInfo.Version < 0x200)
283 #endif
284       {
285          VideoPortDebugPrint(Error, "VBE BIOS present, but incompatible version %d.%d\n",
286                              VBEDeviceExtension->VbeInfo.Version / 0x100,
287                              VBEDeviceExtension->VbeInfo.Version & 0xFF);
288          return FALSE;
289       }
290    }
291    else
292    {
293       VideoPortDebugPrint(Error, "No VBE BIOS found.\n");
294       return FALSE;
295    }
296 
297    /*
298     * Build a mode list here that can be later used by
299     * IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES and IOCTL_VIDEO_QUERY_AVAIL_MODES
300     * calls.
301     */
302 
303    /*
304     * Get the number of supported video modes.
305     *
306     * No need to be map the memory. It's either in the video BIOS memory or
307     * in our trampoline memory. In either case the memory is already mapped.
308     */
309 
310    for (ModeCount = 0; ; ModeCount++)
311    {
312       /* Read the VBE mode number. */
313       VBEDeviceExtension->Int10Interface.Int10ReadMemory(
314          VBEDeviceExtension->Int10Interface.Context,
315          HIWORD(VBEDeviceExtension->VbeInfo.VideoModePtr),
316          LOWORD(VBEDeviceExtension->VbeInfo.VideoModePtr) + (ModeCount << 1),
317          &ModeTemp,
318          sizeof(ModeTemp));
319 
320       /* End of list? */
321       if (ModeTemp == 0xFFFF || ModeTemp == 0)
322          break;
323    }
324 
325    /*
326     * Allocate space for video modes information.
327     */
328 
329    VBEDeviceExtension->ModeInfo =
330       VideoPortAllocatePool(HwDeviceExtension, VpPagedPool, ModeCount * sizeof(VBE_MODEINFO), TAG_VBE);
331    VBEDeviceExtension->ModeNumbers =
332       VideoPortAllocatePool(HwDeviceExtension, VpPagedPool, ModeCount * sizeof(USHORT), TAG_VBE);
333 
334    /*
335     * Get the actual mode infos.
336     */
337 
338    for (CurrentMode = 0, SuitableModeCount = 0;
339         CurrentMode < ModeCount;
340         CurrentMode++)
341    {
342       /* Read the VBE mode number. */
343       VBEDeviceExtension->Int10Interface.Int10ReadMemory(
344          VBEDeviceExtension->Int10Interface.Context,
345          HIWORD(VBEDeviceExtension->VbeInfo.VideoModePtr),
346          LOWORD(VBEDeviceExtension->VbeInfo.VideoModePtr) + (CurrentMode << 1),
347          &ModeTemp,
348          sizeof(ModeTemp));
349 
350       /* Call VBE BIOS to read the mode info. */
351       VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
352       BiosRegisters.Eax = VBE_GET_MODE_INFORMATION;
353       BiosRegisters.Ecx = ModeTemp;
354       BiosRegisters.Edi = VBEDeviceExtension->TrampolineMemoryOffset + 0x200;
355       BiosRegisters.SegEs = VBEDeviceExtension->TrampolineMemorySegment;
356       VBEDeviceExtension->Int10Interface.Int10CallBios(
357          VBEDeviceExtension->Int10Interface.Context,
358          &BiosRegisters);
359 
360       /* Read the VBE mode info. */
361       VBEDeviceExtension->Int10Interface.Int10ReadMemory(
362          VBEDeviceExtension->Int10Interface.Context,
363          VBEDeviceExtension->TrampolineMemorySegment,
364          VBEDeviceExtension->TrampolineMemoryOffset + 0x200,
365          VBEDeviceExtension->ModeInfo + SuitableModeCount,
366          sizeof(VBE_MODEINFO));
367 
368       VbeModeInfo = VBEDeviceExtension->ModeInfo + SuitableModeCount;
369 
370       /* Is this mode acceptable? */
371       if (VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_SUCCESS &&
372           VbeModeInfo->XResolution >= 640 &&
373           VbeModeInfo->YResolution >= 480 &&
374           (VbeModeInfo->MemoryModel == VBE_MEMORYMODEL_PACKEDPIXEL ||
375            VbeModeInfo->MemoryModel == VBE_MEMORYMODEL_DIRECTCOLOR) &&
376           VbeModeInfo->PhysBasePtr != 0)
377       {
378          if (VbeModeInfo->ModeAttributes & VBE_MODEATTR_LINEAR)
379          {
380             /* Bit 15 14 13 12 | 11 10 9 8 | 7 6 5 4 | 3 2 1 0 */
381              // if (ModeTemp & 0x4000)
382              //{
383                 VBEDeviceExtension->ModeNumbers[SuitableModeCount] = ModeTemp | 0x4000;
384                 SuitableModeCount++;
385              //}
386          }
387 #ifdef VBE12_SUPPORT
388          else
389          {
390             VBEDeviceExtension->ModeNumbers[SuitableModeCount] = ModeTemp;
391             SuitableModeCount++;
392          }
393 #endif
394       }
395    }
396 
397 
398    if (SuitableModeCount == 0)
399    {
400 
401       VideoPortDebugPrint(Warn, "VBEMP: No video modes supported\n");
402       return FALSE;
403    }
404 
405    VBEDeviceExtension->ModeCount = SuitableModeCount;
406 
407    /*
408     * Sort the video mode list according to resolution and bits per pixel.
409     */
410 
411    VBESortModes(VBEDeviceExtension);
412 
413    /*
414     * Print the supported video modes.
415     */
416 
417    for (CurrentMode = 0;
418         CurrentMode < SuitableModeCount;
419         CurrentMode++)
420    {
421       VideoPortDebugPrint(Trace, "%dx%dx%d\n",
422          VBEDeviceExtension->ModeInfo[CurrentMode].XResolution,
423          VBEDeviceExtension->ModeInfo[CurrentMode].YResolution,
424          VBEDeviceExtension->ModeInfo[CurrentMode].BitsPerPixel);
425    }
426 
427    /*
428     * Enumerate our children.
429     */
430    VideoPortEnumerateChildren(HwDeviceExtension, NULL);
431 
432    return TRUE;
433 }
434 
435 /*
436  * VBEStartIO
437  *
438  * Processes the specified Video Request Packet.
439  */
440 
441 BOOLEAN NTAPI
442 VBEStartIO(
443    PVOID HwDeviceExtension,
444    PVIDEO_REQUEST_PACKET RequestPacket)
445 {
446    BOOLEAN Result;
447 
448    RequestPacket->StatusBlock->Status = ERROR_INVALID_FUNCTION;
449 
450    switch (RequestPacket->IoControlCode)
451    {
452       case IOCTL_VIDEO_SET_CURRENT_MODE:
453          if (RequestPacket->InputBufferLength < sizeof(VIDEO_MODE))
454          {
455             RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
456             return TRUE;
457          }
458          Result = VBESetCurrentMode(
459             (PVBE_DEVICE_EXTENSION)HwDeviceExtension,
460             (PVIDEO_MODE)RequestPacket->InputBuffer,
461             RequestPacket->StatusBlock);
462          break;
463 
464       case IOCTL_VIDEO_RESET_DEVICE:
465          Result = VBEResetDevice(
466             (PVBE_DEVICE_EXTENSION)HwDeviceExtension,
467             RequestPacket->StatusBlock);
468          break;
469 
470       case IOCTL_VIDEO_MAP_VIDEO_MEMORY:
471          if (RequestPacket->OutputBufferLength < sizeof(VIDEO_MEMORY_INFORMATION) ||
472              RequestPacket->InputBufferLength < sizeof(VIDEO_MEMORY))
473          {
474             RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
475             return TRUE;
476          }
477          Result = VBEMapVideoMemory(
478             (PVBE_DEVICE_EXTENSION)HwDeviceExtension,
479             (PVIDEO_MEMORY)RequestPacket->InputBuffer,
480             (PVIDEO_MEMORY_INFORMATION)RequestPacket->OutputBuffer,
481             RequestPacket->StatusBlock);
482          break;
483 
484       case IOCTL_VIDEO_UNMAP_VIDEO_MEMORY:
485          if (RequestPacket->InputBufferLength < sizeof(VIDEO_MEMORY))
486          {
487             RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
488             return TRUE;
489          }
490          Result = VBEUnmapVideoMemory(
491             (PVBE_DEVICE_EXTENSION)HwDeviceExtension,
492             (PVIDEO_MEMORY)RequestPacket->InputBuffer,
493             RequestPacket->StatusBlock);
494          break;
495 
496       case IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES:
497          if (RequestPacket->OutputBufferLength < sizeof(VIDEO_NUM_MODES))
498          {
499             RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
500             return TRUE;
501          }
502          Result = VBEQueryNumAvailModes(
503             (PVBE_DEVICE_EXTENSION)HwDeviceExtension,
504             (PVIDEO_NUM_MODES)RequestPacket->OutputBuffer,
505             RequestPacket->StatusBlock);
506          break;
507 
508       case IOCTL_VIDEO_QUERY_AVAIL_MODES:
509          if (RequestPacket->OutputBufferLength <
510              ((PVBE_DEVICE_EXTENSION)HwDeviceExtension)->ModeCount * sizeof(VIDEO_MODE_INFORMATION))
511          {
512             RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
513             return TRUE;
514          }
515          Result = VBEQueryAvailModes(
516             (PVBE_DEVICE_EXTENSION)HwDeviceExtension,
517             (PVIDEO_MODE_INFORMATION)RequestPacket->OutputBuffer,
518             RequestPacket->StatusBlock);
519          break;
520 
521       case IOCTL_VIDEO_SET_COLOR_REGISTERS:
522          if (RequestPacket->InputBufferLength < sizeof(VIDEO_CLUT) ||
523              RequestPacket->InputBufferLength <
524              (((PVIDEO_CLUT)RequestPacket->InputBuffer)->NumEntries * sizeof(ULONG)) +
525              FIELD_OFFSET(VIDEO_CLUT, LookupTable))
526          {
527             RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
528             return TRUE;
529          }
530          Result = VBESetColorRegisters(
531             (PVBE_DEVICE_EXTENSION)HwDeviceExtension,
532             (PVIDEO_CLUT)RequestPacket->InputBuffer,
533             RequestPacket->StatusBlock);
534          break;
535 
536       case IOCTL_VIDEO_QUERY_CURRENT_MODE:
537          if (RequestPacket->OutputBufferLength < sizeof(VIDEO_MODE_INFORMATION))
538          {
539             RequestPacket->StatusBlock->Status = ERROR_INSUFFICIENT_BUFFER;
540             return TRUE;
541          }
542          Result = VBEQueryCurrentMode(
543             (PVBE_DEVICE_EXTENSION)HwDeviceExtension,
544             (PVIDEO_MODE_INFORMATION)RequestPacket->OutputBuffer,
545             RequestPacket->StatusBlock);
546          break;
547 
548       default:
549          RequestPacket->StatusBlock->Status = ERROR_INVALID_FUNCTION;
550          return FALSE;
551    }
552 
553    if (Result)
554       RequestPacket->StatusBlock->Status = NO_ERROR;
555 
556    return TRUE;
557 }
558 
559 /*
560  * VBEResetHw
561  *
562  * This function is called to reset the hardware to a known state.
563  */
564 
565 BOOLEAN NTAPI
566 VBEResetHw(
567    PVOID DeviceExtension,
568    ULONG Columns,
569    ULONG Rows)
570 {
571    /* Return FALSE to let HAL reset the display with INT10 */
572    return FALSE;
573 }
574 
575 /*
576  * VBEGetPowerState
577  *
578  * Queries whether the device can support the requested power state.
579  */
580 
581 VP_STATUS NTAPI
582 VBEGetPowerState(
583    PVOID HwDeviceExtension,
584    ULONG HwId,
585    PVIDEO_POWER_MANAGEMENT VideoPowerControl)
586 {
587    INT10_BIOS_ARGUMENTS BiosRegisters;
588    PVBE_DEVICE_EXTENSION VBEDeviceExtension =
589      (PVBE_DEVICE_EXTENSION)HwDeviceExtension;
590 
591    if (HwId != DISPLAY_ADAPTER_HW_ID ||
592        VideoPowerControl->Length < sizeof(VIDEO_POWER_MANAGEMENT))
593       return ERROR_INVALID_FUNCTION;
594 
595    /*
596     * Get general power support information.
597     */
598 
599    VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
600    BiosRegisters.Eax = VBE_POWER_MANAGEMENT_EXTENSIONS;
601    BiosRegisters.Ebx = 0;
602    BiosRegisters.Edi = 0;
603    BiosRegisters.SegEs = 0;
604    VBEDeviceExtension->Int10Interface.Int10CallBios(
605       VBEDeviceExtension->Int10Interface.Context,
606       &BiosRegisters);
607 
608    if ( VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_NOT_SUPPORTED)
609       return ERROR_DEV_NOT_EXIST;
610    if (VBE_GETRETURNCODE(BiosRegisters.Eax) != VBE_SUCCESS)
611       return ERROR_INVALID_FUNCTION;
612 
613    /*
614     * Get current power state.
615     */
616 
617    VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
618    BiosRegisters.Eax = VBE_POWER_MANAGEMENT_EXTENSIONS;
619    BiosRegisters.Ebx = 0x2;
620    BiosRegisters.Edi = 0;
621    BiosRegisters.SegEs = 0;
622    VBEDeviceExtension->Int10Interface.Int10CallBios(
623       VBEDeviceExtension->Int10Interface.Context,
624       &BiosRegisters);
625 
626    if (VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_SUCCESS)
627    {
628       VideoPowerControl->DPMSVersion = BiosRegisters.Ebx & 0xFF;
629       switch (BiosRegisters.Ebx >> 8)
630       {
631          case 0: VideoPowerControl->PowerState = VideoPowerOn; break;
632          case 1: VideoPowerControl->PowerState = VideoPowerStandBy; break;
633          case 2: VideoPowerControl->PowerState = VideoPowerSuspend; break;
634          case 4: VideoPowerControl->PowerState = VideoPowerOff; break;
635          case 5: VideoPowerControl->PowerState = VideoPowerOn; break;
636          default: VideoPowerControl->PowerState = VideoPowerUnspecified;
637       }
638 
639       return NO_ERROR;
640    }
641 
642    return ERROR_DEV_NOT_EXIST;
643 }
644 
645 /*
646  * VBESetPowerState
647  *
648  * Sets the power state of the specified device
649  */
650 
651 VP_STATUS NTAPI
652 VBESetPowerState(
653    PVOID HwDeviceExtension,
654    ULONG HwId,
655    PVIDEO_POWER_MANAGEMENT VideoPowerControl)
656 {
657    INT10_BIOS_ARGUMENTS BiosRegisters;
658    PVBE_DEVICE_EXTENSION VBEDeviceExtension =
659      (PVBE_DEVICE_EXTENSION)HwDeviceExtension;
660 
661    if (HwId != DISPLAY_ADAPTER_HW_ID ||
662        VideoPowerControl->Length < sizeof(VIDEO_POWER_MANAGEMENT) ||
663        VideoPowerControl->PowerState < VideoPowerOn ||
664        VideoPowerControl->PowerState > VideoPowerHibernate)
665       return ERROR_INVALID_FUNCTION;
666 
667    if (VideoPowerControl->PowerState == VideoPowerHibernate)
668       return NO_ERROR;
669 
670    /*
671     * Set current power state.
672     */
673 
674    VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
675    BiosRegisters.Eax = VBE_POWER_MANAGEMENT_EXTENSIONS;
676    BiosRegisters.Ebx = 1;
677    BiosRegisters.Edi = 0;
678    BiosRegisters.SegEs = 0;
679    switch (VideoPowerControl->PowerState)
680    {
681       case VideoPowerStandBy: BiosRegisters.Ebx |= 0x100; break;
682       case VideoPowerSuspend: BiosRegisters.Ebx |= 0x200; break;
683       case VideoPowerOff: BiosRegisters.Ebx |= 0x400; break;
684    }
685 
686    VBEDeviceExtension->Int10Interface.Int10CallBios(
687       VBEDeviceExtension->Int10Interface.Context,
688       &BiosRegisters);
689 
690    if (VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_NOT_SUPPORTED)
691       return ERROR_DEV_NOT_EXIST;
692    if (VBE_GETRETURNCODE(BiosRegisters.Eax) != VBE_SUCCESS)
693       return ERROR_INVALID_FUNCTION;
694 
695    return VBE_SUCCESS;
696 }
697 
698 /*
699  * VBESetCurrentMode
700  *
701  * Sets the adapter to the specified operating mode.
702  */
703 
704 BOOLEAN FASTCALL
705 VBESetCurrentMode(
706    PVBE_DEVICE_EXTENSION DeviceExtension,
707    PVIDEO_MODE RequestedMode,
708    PSTATUS_BLOCK StatusBlock)
709 {
710    INT10_BIOS_ARGUMENTS BiosRegisters;
711 
712    if (RequestedMode->RequestedMode >= DeviceExtension->ModeCount)
713    {
714       return ERROR_INVALID_PARAMETER;
715    }
716 
717    VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
718    BiosRegisters.Eax = VBE_SET_VBE_MODE;
719    BiosRegisters.Ebx = DeviceExtension->ModeNumbers[RequestedMode->RequestedMode];
720    DeviceExtension->Int10Interface.Int10CallBios(
721       DeviceExtension->Int10Interface.Context,
722       &BiosRegisters);
723 
724    if (VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_SUCCESS)
725    {
726       DeviceExtension->CurrentMode = RequestedMode->RequestedMode;
727    }
728    else
729    {
730       VideoPortDebugPrint(Error, "VBEMP: VBESetCurrentMode failed (%x)\n", BiosRegisters.Eax);
731       DeviceExtension->CurrentMode = -1;
732    }
733 
734    return VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_SUCCESS;
735 }
736 
737 /*
738  * VBEResetDevice
739  *
740  * Resets the video hardware to the default mode, to which it was initialized
741  * at system boot.
742  */
743 
744 BOOLEAN FASTCALL
745 VBEResetDevice(
746    PVBE_DEVICE_EXTENSION DeviceExtension,
747    PSTATUS_BLOCK StatusBlock)
748 {
749    INT10_BIOS_ARGUMENTS BiosRegisters;
750 
751    VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
752    BiosRegisters.Eax = VBE_SET_VBE_MODE;
753    BiosRegisters.Ebx = 0x3;
754    DeviceExtension->Int10Interface.Int10CallBios(
755       DeviceExtension->Int10Interface.Context,
756       &BiosRegisters);
757 
758    return VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_SUCCESS;
759 }
760 
761 /*
762  * VBEMapVideoMemory
763  *
764  * Maps the video hardware frame buffer and video RAM into the virtual address
765  * space of the requestor.
766  */
767 
768 BOOLEAN FASTCALL
769 VBEMapVideoMemory(
770    PVBE_DEVICE_EXTENSION DeviceExtension,
771    PVIDEO_MEMORY RequestedAddress,
772    PVIDEO_MEMORY_INFORMATION MapInformation,
773    PSTATUS_BLOCK StatusBlock)
774 {
775    PHYSICAL_ADDRESS FrameBuffer;
776    ULONG inIoSpace = VIDEO_MEMORY_SPACE_MEMORY;
777 
778    StatusBlock->Information = sizeof(VIDEO_MEMORY_INFORMATION);
779 
780    if (DeviceExtension->ModeInfo[DeviceExtension->CurrentMode].ModeAttributes &
781        VBE_MODEATTR_LINEAR)
782    {
783       FrameBuffer.QuadPart =
784          DeviceExtension->ModeInfo[DeviceExtension->CurrentMode].PhysBasePtr;
785       MapInformation->VideoRamBase = RequestedAddress->RequestedVirtualAddress;
786       if (DeviceExtension->VbeInfo.Version < 0x300)
787       {
788          MapInformation->VideoRamLength =
789             DeviceExtension->ModeInfo[DeviceExtension->CurrentMode].BytesPerScanLine *
790             DeviceExtension->ModeInfo[DeviceExtension->CurrentMode].YResolution;
791       }
792       else
793       {
794          MapInformation->VideoRamLength =
795             DeviceExtension->ModeInfo[DeviceExtension->CurrentMode].LinBytesPerScanLine *
796             DeviceExtension->ModeInfo[DeviceExtension->CurrentMode].YResolution;
797       }
798    }
799 #ifdef VBE12_SUPPORT
800    else
801    {
802       FrameBuffer.QuadPart = 0xA0000;
803       MapInformation->VideoRamBase = RequestedAddress->RequestedVirtualAddress;
804       MapInformation->VideoRamLength = 0x10000;
805    }
806 #endif
807 
808    VideoPortMapMemory(DeviceExtension, FrameBuffer,
809       &MapInformation->VideoRamLength, &inIoSpace,
810       &MapInformation->VideoRamBase);
811 
812    MapInformation->FrameBufferBase = MapInformation->VideoRamBase;
813    MapInformation->FrameBufferLength = MapInformation->VideoRamLength;
814 
815    return TRUE;
816 }
817 
818 /*
819  * VBEUnmapVideoMemory
820  *
821  * Releases a mapping between the virtual address space and the adapter's
822  * frame buffer and video RAM.
823  */
824 
825 BOOLEAN FASTCALL
826 VBEUnmapVideoMemory(
827    PVBE_DEVICE_EXTENSION DeviceExtension,
828    PVIDEO_MEMORY VideoMemory,
829    PSTATUS_BLOCK StatusBlock)
830 {
831    VideoPortUnmapMemory(DeviceExtension, VideoMemory->RequestedVirtualAddress,
832       NULL);
833    return TRUE;
834 }
835 
836 /*
837  * VBEQueryNumAvailModes
838  *
839  * Returns the number of video modes supported by the adapter and the size
840  * in bytes of the video mode information, which can be used to allocate a
841  * buffer for an IOCTL_VIDEO_QUERY_AVAIL_MODES request.
842  */
843 
844 BOOLEAN FASTCALL
845 VBEQueryNumAvailModes(
846    PVBE_DEVICE_EXTENSION DeviceExtension,
847    PVIDEO_NUM_MODES Modes,
848    PSTATUS_BLOCK StatusBlock)
849 {
850    Modes->NumModes = DeviceExtension->ModeCount;
851    Modes->ModeInformationLength = sizeof(VIDEO_MODE_INFORMATION);
852    StatusBlock->Information = sizeof(VIDEO_NUM_MODES);
853    return TRUE;
854 }
855 
856 /*
857  * VBEQueryMode
858  *
859  * Returns information about one particular video mode.
860  */
861 
862 VOID FASTCALL
863 VBEQueryMode(
864    PVBE_DEVICE_EXTENSION DeviceExtension,
865    PVIDEO_MODE_INFORMATION VideoMode,
866    ULONG VideoModeId)
867 {
868    PVBE_MODEINFO VBEMode = &DeviceExtension->ModeInfo[VideoModeId];
869    ULONG dpi;
870 
871    VideoMode->Length = sizeof(VIDEO_MODE_INFORMATION);
872    VideoMode->ModeIndex = VideoModeId;
873    VideoMode->VisScreenWidth = VBEMode->XResolution;
874    VideoMode->VisScreenHeight = VBEMode->YResolution;
875    if (DeviceExtension->VbeInfo.Version < 0x300)
876       VideoMode->ScreenStride = VBEMode->BytesPerScanLine;
877    else
878       VideoMode->ScreenStride = VBEMode->LinBytesPerScanLine;
879    VideoMode->NumberOfPlanes = VBEMode->NumberOfPlanes;
880    VideoMode->BitsPerPlane = VBEMode->BitsPerPixel / VBEMode->NumberOfPlanes;
881    VideoMode->Frequency = 1;
882 
883    /* Assume 96DPI and 25.4 millimeters per inch, round to nearest */
884    dpi = 96;
885    VideoMode->XMillimeter = ((ULONGLONG)VBEMode->XResolution * 254 + (dpi * 5)) / (dpi * 10);
886    VideoMode->YMillimeter = ((ULONGLONG)VBEMode->YResolution * 254 + (dpi * 5)) / (dpi * 10);
887 
888    if (VBEMode->BitsPerPixel > 8)
889    {
890       /*
891        * Always report 16bpp modes and not 15bpp mode...
892        */
893       if (VBEMode->BitsPerPixel == 15 && VBEMode->NumberOfPlanes == 1)
894       {
895          VideoMode->BitsPerPlane = 16;
896       }
897 
898       if (DeviceExtension->VbeInfo.Version < 0x300)
899       {
900          VideoMode->NumberRedBits = VBEMode->RedMaskSize;
901          VideoMode->NumberGreenBits = VBEMode->GreenMaskSize;
902          VideoMode->NumberBlueBits = VBEMode->BlueMaskSize;
903          VideoMode->RedMask = ((1 << VBEMode->RedMaskSize) - 1) << VBEMode->RedFieldPosition;
904          VideoMode->GreenMask = ((1 << VBEMode->GreenMaskSize) - 1) << VBEMode->GreenFieldPosition;
905          VideoMode->BlueMask = ((1 << VBEMode->BlueMaskSize) - 1) << VBEMode->BlueFieldPosition;
906       }
907       else
908       {
909          VideoMode->NumberRedBits = VBEMode->LinRedMaskSize;
910          VideoMode->NumberGreenBits = VBEMode->LinGreenMaskSize;
911          VideoMode->NumberBlueBits = VBEMode->LinBlueMaskSize;
912          VideoMode->RedMask = ((1 << VBEMode->LinRedMaskSize) - 1) << VBEMode->LinRedFieldPosition;
913          VideoMode->GreenMask = ((1 << VBEMode->LinGreenMaskSize) - 1) << VBEMode->LinGreenFieldPosition;
914          VideoMode->BlueMask = ((1 << VBEMode->LinBlueMaskSize) - 1) << VBEMode->LinBlueFieldPosition;
915       }
916    }
917    else
918    {
919       VideoMode->NumberRedBits =
920       VideoMode->NumberGreenBits =
921       VideoMode->NumberBlueBits = 6;
922       VideoMode->RedMask =
923       VideoMode->GreenMask =
924       VideoMode->BlueMask = 0;
925    }
926    VideoMode->VideoMemoryBitmapWidth = VBEMode->XResolution;
927    VideoMode->VideoMemoryBitmapHeight = VBEMode->YResolution;
928    VideoMode->AttributeFlags = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR |
929       VIDEO_MODE_NO_OFF_SCREEN;
930    if (VideoMode->BitsPerPlane <= 8)
931       VideoMode->AttributeFlags |= VIDEO_MODE_PALETTE_DRIVEN;
932    VideoMode->DriverSpecificAttributeFlags = 0;
933 }
934 
935 /*
936  * VBEQueryAvailModes
937  *
938  * Returns information about each video mode supported by the adapter.
939  */
940 
941 BOOLEAN FASTCALL
942 VBEQueryAvailModes(
943    PVBE_DEVICE_EXTENSION DeviceExtension,
944    PVIDEO_MODE_INFORMATION ReturnedModes,
945    PSTATUS_BLOCK StatusBlock)
946 {
947    ULONG CurrentModeId;
948    PVIDEO_MODE_INFORMATION CurrentMode;
949    PVBE_MODEINFO CurrentVBEMode;
950 
951    for (CurrentModeId = 0, CurrentMode = ReturnedModes,
952         CurrentVBEMode = DeviceExtension->ModeInfo;
953         CurrentModeId < DeviceExtension->ModeCount;
954         CurrentModeId++, CurrentMode++, CurrentVBEMode++)
955    {
956       VBEQueryMode(DeviceExtension, CurrentMode, CurrentModeId);
957    }
958 
959    StatusBlock->Information =
960       sizeof(VIDEO_MODE_INFORMATION) * DeviceExtension->ModeCount;
961 
962    return TRUE;
963 }
964 
965 /*
966  * VBEQueryCurrentMode
967  *
968  * Returns information about current video mode.
969  */
970 
971 BOOLEAN FASTCALL
972 VBEQueryCurrentMode(
973    PVBE_DEVICE_EXTENSION DeviceExtension,
974    PVIDEO_MODE_INFORMATION VideoModeInfo,
975    PSTATUS_BLOCK StatusBlock)
976 {
977    StatusBlock->Information = sizeof(VIDEO_MODE_INFORMATION);
978 
979    VBEQueryMode(
980       DeviceExtension,
981       VideoModeInfo,
982       DeviceExtension->CurrentMode);
983 
984    return TRUE;
985 }
986 
987 /*
988  * VBESetColorRegisters
989  *
990  * Sets the adapter's color registers to the specified RGB values. There
991  * are code paths in this function, one generic and one for VGA compatible
992  * controllers. The latter is needed for Bochs, where the generic one isn't
993  * yet implemented.
994  */
995 
996 BOOLEAN FASTCALL
997 VBESetColorRegisters(
998    PVBE_DEVICE_EXTENSION DeviceExtension,
999    PVIDEO_CLUT ColorLookUpTable,
1000    PSTATUS_BLOCK StatusBlock)
1001 {
1002    INT10_BIOS_ARGUMENTS BiosRegisters;
1003    ULONG Entry;
1004    PULONG OutputEntry;
1005    ULONG OutputBuffer[256];
1006 
1007    if (ColorLookUpTable->NumEntries + ColorLookUpTable->FirstEntry > 256)
1008       return FALSE;
1009 
1010    /*
1011     * For VGA compatible adapters program the color registers directly.
1012     */
1013 
1014    if (!(DeviceExtension->VbeInfo.Capabilities & 2))
1015    {
1016       for (Entry = ColorLookUpTable->FirstEntry;
1017            Entry < ColorLookUpTable->NumEntries + ColorLookUpTable->FirstEntry;
1018            Entry++)
1019       {
1020          VideoPortWritePortUchar((PUCHAR)0x03c8, Entry);
1021          VideoPortWritePortUchar((PUCHAR)0x03c9, ColorLookUpTable->LookupTable[Entry].RgbArray.Red);
1022          VideoPortWritePortUchar((PUCHAR)0x03c9, ColorLookUpTable->LookupTable[Entry].RgbArray.Green);
1023          VideoPortWritePortUchar((PUCHAR)0x03c9, ColorLookUpTable->LookupTable[Entry].RgbArray.Blue);
1024       }
1025 
1026       return TRUE;
1027    }
1028    else
1029    {
1030       /*
1031        * We can't just copy the values, because we need to swap the Red
1032        * and Blue values.
1033        */
1034 
1035       for (Entry = ColorLookUpTable->FirstEntry,
1036            OutputEntry = OutputBuffer;
1037            Entry < ColorLookUpTable->NumEntries + ColorLookUpTable->FirstEntry;
1038            Entry++, OutputEntry++)
1039       {
1040          *OutputEntry =
1041             (ColorLookUpTable->LookupTable[Entry].RgbArray.Red << 16) |
1042             (ColorLookUpTable->LookupTable[Entry].RgbArray.Green << 8) |
1043             (ColorLookUpTable->LookupTable[Entry].RgbArray.Blue);
1044       }
1045 
1046       DeviceExtension->Int10Interface.Int10WriteMemory(
1047          DeviceExtension->Int10Interface.Context,
1048          DeviceExtension->TrampolineMemorySegment,
1049          DeviceExtension->TrampolineMemoryOffset,
1050          OutputBuffer,
1051          (OutputEntry - OutputBuffer) * sizeof(ULONG));
1052 
1053       VideoPortZeroMemory(&BiosRegisters, sizeof(BiosRegisters));
1054       BiosRegisters.Eax = VBE_SET_GET_PALETTE_DATA;
1055       BiosRegisters.Ebx = 0;
1056       BiosRegisters.Ecx = ColorLookUpTable->NumEntries;
1057       BiosRegisters.Edx = ColorLookUpTable->FirstEntry;
1058       BiosRegisters.Edi = DeviceExtension->TrampolineMemoryOffset;
1059       BiosRegisters.SegEs = DeviceExtension->TrampolineMemorySegment;
1060       DeviceExtension->Int10Interface.Int10CallBios(
1061          DeviceExtension->Int10Interface.Context,
1062          &BiosRegisters);
1063 
1064       return VBE_GETRETURNCODE(BiosRegisters.Eax) == VBE_SUCCESS;
1065    }
1066 }
1067