Patch by Jan Klötzke with minor changes by myself:

* Use vm86 mode to call the VESA BIOS to do the actual mode switching by
  providing an ioctl in the vesa driver.
* Fix vm86.h.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25680 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2008-05-28 10:48:01 +00:00
parent 2c8b410ad5
commit 9f16184577
8 changed files with 155 additions and 53 deletions

View File

@ -14,6 +14,7 @@
#define VESA_MODES_BOOT_INFO "vesa_modes/v1"
struct vesa_mode {
uint16 mode;
uint16 width;
uint16 height;
uint8 bits_per_pixel;
@ -26,7 +27,6 @@ struct vesa_shared_info {
display_mode current_mode;
uint32 bytes_per_row;
area_id registers_area; // area of memory mapped registers
area_id frame_buffer_area; // area of frame buffer
uint8 *frame_buffer;
// pointer to frame buffer (visible by all apps!)
@ -42,6 +42,7 @@ struct vesa_shared_info {
enum {
VESA_GET_PRIVATE_DATA = B_DEVICE_OP_CODES_END + 1,
VESA_GET_DEVICE_NAME,
VESA_SET_DISPLAY_MODE,
VGA_SET_INDEXED_COLORS,
VGA_PLANAR_BLIT,

View File

@ -6,7 +6,7 @@
#define _VM86_H
#include <OS.h>
#include <arch_cpu.h>
#include <arch/x86/arch_cpu.h>
#ifdef __cplusplus
extern "C" {

View File

@ -137,7 +137,17 @@ vesa_set_display_mode(display_mode *_mode)
if (vesa_propose_display_mode(&mode, &mode, &mode) != B_OK)
return B_BAD_VALUE;
// ToDo: unless we have a VBE3 device, we can't change the display mode
vesa_mode *modes = gInfo->vesa_modes;
for (unsigned int i = gInfo->shared_info->vesa_mode_count; i-- > 0;) {
// search mode in VESA mode list
// TODO: list is ordered, we could use binary search
if (modes[i].width == mode.virtual_width
&& modes[i].height == mode.virtual_height
&& get_color_space_for_depth(modes[i].bits_per_pixel)
== mode.space)
return ioctl(gInfo->device, VESA_SET_DISPLAY_MODE, &i, sizeof(i));
}
return B_UNSUPPORTED;
}

View File

@ -149,6 +149,17 @@ device_ioctl(void *cookie, uint32 msg, void *buffer, size_t bufferLength)
return B_OK;
case VESA_SET_DISPLAY_MODE:
{
unsigned int mode;
if (bufferLength != sizeof(mode)
|| user_memcpy(&mode, buffer, sizeof(mode)) < B_OK)
return B_BAD_ADDRESS;
return vesa_set_display_mode(*info, mode);
}
case VGA_SET_INDEXED_COLORS:
{
vga_set_indexed_colors_args args;

View File

@ -5,67 +5,20 @@
#include "vesa_private.h"
#include "vesa.h"
#include <string.h>
#include <boot_item.h>
#include <frame_buffer_console.h>
#include <util/kernel_cpp.h>
#include <arch/x86/vm86.h>
#include "driver.h"
#include "utility.h"
#include "vesa_info.h"
class PhysicalMemoryMapper {
public:
PhysicalMemoryMapper();
~PhysicalMemoryMapper();
area_id Map(const char *name, void *physicalAddress, size_t numBytes,
uint32 spec, uint32 protection, void **virtualAddress);
status_t InitCheck() { return fArea < B_OK ? (status_t)fArea : B_OK; }
void Keep();
private:
area_id fArea;
};
PhysicalMemoryMapper::PhysicalMemoryMapper()
:
fArea(-1)
{
}
PhysicalMemoryMapper::~PhysicalMemoryMapper()
{
if (fArea >= B_OK)
delete_area(fArea);
}
area_id
PhysicalMemoryMapper::Map(const char *name, void *physicalAddress,
size_t numBytes, uint32 spec, uint32 protection, void **virtualAddress)
{
fArea = map_physical_memory(name, physicalAddress, numBytes, spec,
protection, virtualAddress);
return fArea;
}
void
PhysicalMemoryMapper::Keep()
{
fArea = -1;
}
// #pragma mark -
static uint32
get_color_space_for_depth(uint32 depth)
{
@ -89,6 +42,57 @@ get_color_space_for_depth(uint32 depth)
}
static status_t
vbe_get_mode_info(struct vm86_state *vmState, uint16 mode,
struct vbe_mode_info *modeInfo)
{
struct vbe_mode_info *vbeModeInfo = (struct vbe_mode_info *)0x1000;
memset(vbeModeInfo, 0, sizeof(vbe_mode_info));
vmState->regs.eax = 0x4f01;
vmState->regs.ecx = mode;
vmState->regs.es = 0x1000 >> 4;
vmState->regs.edi = 0x0000;
status_t status = vm86_do_int(vmState, 0x10);
if (status != B_OK) {
dprintf(DEVICE_NAME ": vbe_get_mode_info(%u): vm86 failed\n", mode);
return status;
}
if ((vmState->regs.eax & 0xffff) != 0x4f) {
dprintf(DEVICE_NAME ": vbe_get_mode_info(): BIOS returned 0x%04lx\n",
vmState->regs.eax & 0xffff);
return B_ENTRY_NOT_FOUND;
}
memcpy(modeInfo, vbeModeInfo, sizeof(struct vbe_mode_info));
return B_OK;
}
static status_t
vbe_set_mode(struct vm86_state *vmState, uint16 mode)
{
vmState->regs.eax = 0x4f02;
vmState->regs.ebx = (mode & SET_MODE_MASK) | SET_MODE_LINEAR_BUFFER;
status_t status = vm86_do_int(vmState, 0x10);
if (status != B_OK) {
dprintf(DEVICE_NAME ": vbe_set_mode(%u): vm86 failed\n", mode);
return status;
}
if ((vmState->regs.eax & 0xffff) != 0x4f) {
dprintf(DEVICE_NAME ": vbe_set_mode(): BIOS returned 0x%04lx\n",
vmState->regs.eax & 0xffff);
return B_ERROR;
}
return B_OK;
}
// #pragma mark -
@ -103,6 +107,7 @@ vesa_init(vesa_info &info)
size_t modesSize = 0;
vesa_mode *modes = (vesa_mode *)get_boot_item(VESA_MODES_BOOT_INFO,
&modesSize);
info.modes = modes;
size_t sharedSize = (sizeof(vesa_shared_info) + 7) & ~7;
@ -115,7 +120,7 @@ vesa_init(vesa_info &info)
vesa_shared_info &sharedInfo = *info.shared_info;
memset(/*(void *)*/&sharedInfo, 0, sizeof(vesa_shared_info));
memset(&sharedInfo, 0, sizeof(vesa_shared_info));
if (modes != NULL) {
sharedInfo.vesa_mode_offset = sharedSize;
@ -152,3 +157,73 @@ vesa_uninit(vesa_info &info)
delete_area(info.shared_area);
}
status_t
vesa_set_display_mode(vesa_info &info, unsigned int mode)
{
if (mode >= info.shared_info->vesa_mode_count)
return B_ENTRY_NOT_FOUND;
// Prepare vm86 mode environment
struct vm86_state vmState;
status_t status = vm86_prepare(&vmState, 0x2000);
if (status != B_OK) {
dprintf(DEVICE_NAME": vesa_set_display_mode(): vm86_prepare failed\n");
return status;
}
area_id newFBArea;
frame_buffer_boot_info *bufferInfo;
struct vbe_mode_info modeInfo;
// Get mode information
status = vbe_get_mode_info(&vmState, info.modes[mode].mode, &modeInfo);
if (status != B_OK) {
dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot get mode info\n");
goto error;
}
// Set mode
status = vbe_set_mode(&vmState, info.modes[mode].mode);
if (status != B_OK) {
dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot set mode\n");
goto error;
}
// Map new frame buffer
void *frameBuffer;
newFBArea = map_physical_memory("vesa_fb",
(void *)modeInfo.physical_base,
modeInfo.bytes_per_row * modeInfo.height, B_ANY_KERNEL_ADDRESS,
B_READ_AREA | B_WRITE_AREA, &frameBuffer);
if (newFBArea < B_OK) {
status = (status_t)newFBArea;
goto error;
}
delete_area(info.shared_info->frame_buffer_area);
// Update shared frame buffer information
info.shared_info->frame_buffer_area = newFBArea;
info.shared_info->frame_buffer = (uint8 *)frameBuffer;
info.shared_info->physical_frame_buffer = (uint8 *)modeInfo.physical_base;
info.shared_info->bytes_per_row = modeInfo.bytes_per_row;
info.shared_info->current_mode.virtual_width = modeInfo.width;
info.shared_info->current_mode.virtual_height = modeInfo.height;
info.shared_info->current_mode.space = get_color_space_for_depth(
modeInfo.bits_per_pixel);
// Update boot item as it's used in vesa_init()
bufferInfo
= (frame_buffer_boot_info *)get_boot_item(FRAME_BUFFER_BOOT_INFO, NULL);
bufferInfo->area = newFBArea;
bufferInfo->frame_buffer = (addr_t)frameBuffer;
bufferInfo->width = modeInfo.width;
bufferInfo->height = modeInfo.height;
bufferInfo->depth = modeInfo.bits_per_pixel;
bufferInfo->bytes_per_row = modeInfo.bytes_per_row;
error:
vm86_cleanup(&vmState);
return status;
}

View File

@ -16,6 +16,7 @@
struct vesa_get_supported_modes;
struct vesa_mode;
struct vesa_info {
uint32 cookie_magic;
@ -24,9 +25,11 @@ struct vesa_info {
pci_info *pci;
struct vesa_shared_info *shared_info;
area_id shared_area;
vesa_mode *modes;
};
extern status_t vesa_init(vesa_info &info);
extern void vesa_uninit(vesa_info &info);
extern status_t vesa_set_display_mode(vesa_info &info, unsigned int mode);
#endif /* VESA_PRIVATE_H */

View File

@ -1007,6 +1007,7 @@ platform_init_video(void)
uint32 i = 0;
while ((mode = (video_mode *)list_get_next_item(&sModeList, mode))
!= NULL) {
modes[i].mode = mode->mode;
modes[i].width = mode->width;
modes[i].height = mode->height;
modes[i].bits_per_pixel = mode->bits_per_pixel;

View File

@ -410,6 +410,7 @@ frame_buffer_console_init(kernel_args *args)
args->frame_buffer.height, args->frame_buffer.depth,
args->frame_buffer.bytes_per_row);
sBootInfo.area = sConsole.area;
sBootInfo.frame_buffer = (addr_t)frameBuffer;
sBootInfo.width = args->frame_buffer.width;
sBootInfo.height = args->frame_buffer.height;