video modes
[efitest1] / test.c
diff --git a/test.c b/test.c
index 2b80863..5dbbed7 100644 (file)
--- a/test.c
+++ b/test.c
 #include <efilib.h>
 #include <efiprot.h>
 
+int find_best_video_mode(void);
+int find_video_mode(int xres, int yres, int bpp);
+int set_video_mode(int mode);
+int cur_video_mode(void);
+void draw_box(int x, int y, int w, int h, int r, int g, int b);
+int mask_to_shift(unsigned int mask);
+int mask_to_bpp(unsigned int mask);
+int mode_bpp(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *minf);
+
+uint32_t *fb;
+int fb_width, fb_height;
+int scanwidth;
+int rshift, bshift, gshift;
+unsigned int rmask, bmask, gmask;
+EFI_GRAPHICS_OUTPUT_PROTOCOL *gop;
+
 EFI_STATUS EFIAPI efi_main(EFI_HANDLE img, EFI_SYSTEM_TABLE *systab)
 {
+       int mode;
+       EFI_HANDLE *handles;
+       unsigned long nhandles = 0;
+       unsigned long misize;
+       EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *mi;
+       SIMPLE_TEXT_OUTPUT_INTERFACE *sout;
+
        InitializeLib(img, systab);
+
+       sout = systab->ConOut;
+       uefi_call_wrapper(sout->ClearScreen, 1, sout);
        Print(L"hello world\n");
 
+       LibLocateHandle(ByProtocol, &GraphicsOutputProtocol, 0, &nhandles, 0);
+       if(nhandles <= 0) {
+               Print(L"Failed to locate any graphics output devices\n");
+               goto end;
+       }
+       if(!(handles = AllocatePool(nhandles))) {
+               Print(L"Failed to allocate memory for %lu handles\n", nhandles);
+               goto end;
+       }
+       Print(L"Found %lu graphics output devices\n", nhandles);
+       if(LibLocateHandle(ByProtocol, &GraphicsOutputProtocol, 0, &nhandles, &handles) != 0) {
+               Print(L"Failed to retreive graphics output device handles\n");
+               goto end;
+       }
+       if(uefi_call_wrapper(BS->HandleProtocol, 3, handles[0], &GraphicsOutputProtocol, &gop) != 0) {
+               Print(L"Failed to retreive graphics output protocol structure\n");
+               goto end;
+       }
+
+
+       if((mode = find_best_video_mode()) == -1) {
+               Print(L"Failed to find best video mode, leaving as is\n");
+       } else {
+               if(set_video_mode(mode) == -1) {
+                       Print(L"Failed to change video mode, leaving as is\n");
+               }
+       }
+       uefi_call_wrapper(gop->QueryMode, 4, gop, gop->Mode->Mode, &misize, &mi);
+
+       fb_width = mi->HorizontalResolution;
+       fb_height = mi->VerticalResolution;
+       scanwidth = mi->PixelsPerScanLine;
+
+       switch(mi->PixelFormat) {
+       case PixelRedGreenBlueReserved8BitPerColor:
+               rshift = 0;
+               gshift = 8;
+               bshift = 16;
+               rmask = 0xff;
+               gmask = 0xff00;
+               bmask = 0xff0000;
+               break;
+
+       case PixelBlueGreenRedReserved8BitPerColor:
+               rshift = 16;
+               gshift = 8;
+               bshift = 0;
+               rmask = 0xff0000;
+               gmask = 0xff00;
+               bmask = 0xff;
+               break;
+
+       case PixelBitMask:
+               rmask = mi->PixelInformation.RedMask;
+               gmask = mi->PixelInformation.GreenMask;
+               bmask = mi->PixelInformation.BlueMask;
+               rshift = mask_to_shift(rmask);
+               gshift = mask_to_shift(gmask);
+               bshift = mask_to_shift(bmask);
+               break;
+
+       default:
+               Print(L"Unsupported pixel format\n");
+               goto end;
+       }
+       fb = (uint32_t*)gop->Mode->FrameBufferBase;
+
+       Print(L"Video mode %ux%u %dbpp (fmt id: %u)\n", mi->HorizontalResolution,
+                       mi->VerticalResolution, mask_to_bpp(rmask | gmask | bmask), (unsigned int)mi->PixelFormat);
+
+       draw_box(100, 100, 200, 200, 255, 128, 64);
+
+end:
        asm volatile("cli");
        for(;;) {
                asm volatile("hlt");
@@ -14,3 +113,123 @@ EFI_STATUS EFIAPI efi_main(EFI_HANDLE img, EFI_SYSTEM_TABLE *systab)
 
        return EFI_SUCCESS;
 }
+
+static uint32_t calc_score(int x, int y, int bpp)
+{
+       uint32_t a = y >> 5;
+       uint32_t b = x >> 5;
+
+       if(a > 0xfff) a = 0xfff;
+       if(b > 0xfff) b = 0xfff;
+
+       return (a << 20) | (b << 8) | (bpp & 0xff);
+}
+
+int find_best_video_mode(void)
+{
+       int i, nmodes, w, h;
+       int score, best_score = 0, best_mode = -1;
+       unsigned long misize;
+       EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *mi;
+
+       nmodes = gop->Mode->MaxMode;
+       for(i=0; i<nmodes; i++) {
+               uefi_call_wrapper(gop->QueryMode, 4, gop, i, &misize, &mi);
+               w = mi->HorizontalResolution;
+               h = mi->VerticalResolution;
+
+               score = calc_score(w, h, mode_bpp(mi));
+               if(score > best_score) {
+                       best_score = score;
+                       best_mode = i;
+               }
+       }
+       return best_mode;
+}
+
+int find_video_mode(int xres, int yres, int bpp)
+{
+       int i, nmodes, w, h;
+       unsigned long misize;
+       EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *mi;
+
+       nmodes = gop->Mode->MaxMode;
+       for(i=0; i<nmodes; i++) {
+               uefi_call_wrapper(gop->QueryMode, 4, gop, i, &misize, &mi);
+               w = mi->HorizontalResolution;
+               h = mi->VerticalResolution;
+               if(w == xres && h == yres && mode_bpp(mi) >= bpp) {
+                       return i;
+               }
+       }
+       return -1;
+}
+
+int set_video_mode(int mode)
+{
+       if(uefi_call_wrapper(gop->SetMode, 2, gop, mode) != 0) {
+               return -1;
+       }
+       return 0;
+}
+
+int cur_video_mode(void)
+{
+       return gop->Mode->Mode;
+}
+
+void draw_box(int x, int y, int w, int h, int r, int g, int b)
+{
+       int i, j;
+       uint32_t pixcol;
+       uint32_t *pptr = fb + y * scanwidth + x;
+
+       pixcol = (((uint32_t)r << rshift) & rmask) |
+               (((uint32_t)g << gshift) & gmask) |
+               (((uint32_t)b << bshift) & bmask);
+
+       for(i=0; i<h; i++) {
+               for(j=0; j<w; j++) {
+                       *pptr++ = pixcol;
+               }
+               pptr += scanwidth - w;
+       }
+}
+
+int mask_to_shift(unsigned int mask)
+{
+       int res = 0;
+       while((mask & 1) == 0) {
+               mask >>= 1;
+               res++;
+       }
+       return res;
+}
+
+int mask_to_bpp(unsigned int mask)
+{
+       int res = 0;
+
+       while(mask) {
+               mask >>= 1;
+               res++;
+       }
+       return res;
+}
+
+int mode_bpp(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *minf)
+{
+       switch(minf->PixelFormat) {
+       case PixelRedGreenBlueReserved8BitPerColor:
+       case PixelBlueGreenRedReserved8BitPerColor:
+               return 24;
+
+       case PixelBitMask:
+               return mask_to_bpp(minf->PixelInformation.RedMask | minf->PixelInformation.GreenMask |
+                               minf->PixelInformation.BlueMask);
+
+       default:
+               break;
+       }
+       return 0;
+}