driver.cpp revision a0a3a26a4065b9401681a8c99a11bd83e08f94cc
/* $Id$ */
/** @file
* VBoxVideo driver, Haiku Guest Additions, implementation.
*/
/*
* Copyright (C) 2012 Oracle-2013 Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*
* This code is based on:
*
* VirtualBox Guest Additions for Haiku.
* Copyright (c) 2011 Mike Smith <mike@scgtrp.net>
* Fran�ois Revol <revol@free.fr>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <KernelExport.h>
#include <PCI.h>
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <graphic_driver.h>
#include <VBoxGuest-haiku.h>
#include <VBox/VBoxVideoGuest.h>
#include "../common/VBoxVideo_common.h"
#define VENDOR_ID 0x80ee
#define DEVICE_ID 0xbeef
#define DRIVER_NAME "VBoxVideoDriver"
#define DEVICE_FORMAT "vd_%04X_%04X_%02X%02X%02X"
/** @todo r=ramshankar: pretty sure IPRT has something for page rounding,
* replace with IPRT version later. */
#define ROUND_TO_PAGE_SIZE(x) (((x) + (B_PAGE_SIZE) - 1) & ~((B_PAGE_SIZE) - 1))
#define ENABLE_DEBUG_TRACE
#undef TRACE
#ifdef ENABLE_DEBUG_TRACE
#define TRACE(x...) dprintf("VBoxVideo: " x)
#else
#define TRACE(x...) ;
#endif
int32 api_version = B_CUR_DRIVER_API_VERSION; // revision of driver API we support
extern "C" status_t vm_set_area_memory_type(area_id id, phys_addr_t physicalBase, uint32 type);
struct Benaphore
{
sem_id sem;
int32 count;
status_t Init(const char *name)
{
count = 0;
sem = create_sem(0, name);
return sem < 0 ? sem : B_OK;
}
status_t Acquire()
{
if (atomic_add(&count, 1) > 0)
return acquire_sem(sem);
return B_OK;
}
status_t Release()
{
if (atomic_add(&count, -1) > 1)
return release_sem(sem);
return B_OK;
}
void Delete()
{
delete_sem(sem);
}
};
struct DeviceInfo
{
uint32 openCount; /* Count of how many times device has been opened */
uint32 flags; /* Device flags */
area_id sharedArea; /* Area shared between driver and all accelerants */
SharedInfo *sharedInfo; /* Pointer to shared info area memory */
pci_info pciInfo; /* Copy of pci info for this device */
char name[B_OS_NAME_LENGTH]; /* Name of device */
};
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
status_t device_open(const char *name, uint32 flags, void **cookie);
status_t device_close(void *dev);
status_t device_free(void *dev);
status_t device_read(void *dev, off_t pos, void *buf, size_t *len);
status_t device_write(void *dev, off_t pos, const void *buf, size_t *len);
status_t device_ioctl(void *dev, uint32 msg, void *buf, size_t len);
static uint32 get_color_space_for_depth(uint32 depth);
/*******************************************************************************
* Globals *
*******************************************************************************/
/* At most one virtual video card ever appears, no reason for this to be an array */
static DeviceInfo gDeviceInfo;
static char *gDeviceNames[2] = { gDeviceInfo.name, NULL };
static bool gCanHasDevice = false; /* is the device present? */
static Benaphore gLock;
static pci_module_info *gPCI;
static device_hooks gDeviceHooks =
{
device_open,
device_close,
device_free,
device_ioctl,
device_read,
device_write,
NULL, /* select */
NULL, /* deselect */
NULL, /* read_pages */
NULL /* write_pages */
};
status_t init_hardware()
{
LogFlowFunc(("init_hardware\n"));
status_t err = get_module(VBOXGUEST_MODULE_NAME, (module_info **)&g_VBoxGuest);
if (err == B_OK)
{
err = get_module(B_PCI_MODULE_NAME, (module_info **)&gPCI);
if (err == B_OK)
return B_OK;
LogRel((DRIVER_NAME ":_init_hardware() get_module(%s) failed. err=%08lx\n", B_PCI_MODULE_NAME));
}
else
LogRel((DRIVER_NAME ":_init_hardware() get_module(%s) failed. err=%08lx\n", VBOXGUEST_MODULE_NAME, err));
return B_ERROR;
}
status_t init_driver()
{
LogFlowFunc(("init_driver\n"));
gLock.Init("VBoxVideo driver lock");
uint32 pciIndex = 0;
while (gPCI->get_nth_pci_info(pciIndex, &gDeviceInfo.pciInfo) == B_OK)
{
if (gDeviceInfo.pciInfo.vendor_id == VENDOR_ID && gDeviceInfo.pciInfo.device_id == DEVICE_ID)
{
sprintf(gDeviceInfo.name, "graphics/" DEVICE_FORMAT,
gDeviceInfo.pciInfo.vendor_id, gDeviceInfo.pciInfo.device_id,
gDeviceInfo.pciInfo.bus, gDeviceInfo.pciInfo.device, gDeviceInfo.pciInfo.function);
TRACE("found device %s\n", gDeviceInfo.name);
gCanHasDevice = true;
gDeviceInfo.openCount = 0;
size_t sharedSize = (sizeof(SharedInfo) + 7) & ~7;
gDeviceInfo.sharedArea = create_area("vboxvideo shared info",
(void **)&gDeviceInfo.sharedInfo, B_ANY_KERNEL_ADDRESS,
ROUND_TO_PAGE_SIZE(sharedSize), B_FULL_LOCK,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_USER_CLONEABLE_AREA);
uint16_t width, height, vwidth, bpp, flags;
VBoxVideoGetModeRegisters(&width, &height, &vwidth, &bpp, &flags);
gDeviceInfo.sharedInfo->currentMode.space = get_color_space_for_depth(bpp);
gDeviceInfo.sharedInfo->currentMode.virtual_width = width;
gDeviceInfo.sharedInfo->currentMode.virtual_height = height;
gDeviceInfo.sharedInfo->currentMode.h_display_start = 0;
gDeviceInfo.sharedInfo->currentMode.v_display_start = 0;
gDeviceInfo.sharedInfo->currentMode.flags = 0;
gDeviceInfo.sharedInfo->currentMode.timing.h_display = width;
gDeviceInfo.sharedInfo->currentMode.timing.v_display = height;
/* Not used, but this makes a reasonable-sounding refresh rate show in screen prefs: */
gDeviceInfo.sharedInfo->currentMode.timing.h_total = 1000;
gDeviceInfo.sharedInfo->currentMode.timing.v_total = 1;
gDeviceInfo.sharedInfo->currentMode.timing.pixel_clock = 850;
/* Map the PCI memory space */
uint32 command_reg = gPCI->read_pci_config(gDeviceInfo.pciInfo.bus,
gDeviceInfo.pciInfo.device, gDeviceInfo.pciInfo.function, PCI_command, 2);
command_reg |= PCI_command_io | PCI_command_memory | PCI_command_master;
gPCI->write_pci_config(gDeviceInfo.pciInfo.bus, gDeviceInfo.pciInfo.device,
gDeviceInfo.pciInfo.function, PCI_command, 2, command_reg);
gDeviceInfo.sharedInfo->framebufferArea = map_physical_memory("vboxvideo framebuffer",
(phys_addr_t)gDeviceInfo.pciInfo.u.h0.base_registers[0],
gDeviceInfo.pciInfo.u.h0.base_register_sizes[0], B_ANY_KERNEL_BLOCK_ADDRESS,
B_READ_AREA | B_WRITE_AREA, &(gDeviceInfo.sharedInfo->framebuffer));
vm_set_area_memory_type(gDeviceInfo.sharedInfo->framebufferArea,
(phys_addr_t)gDeviceInfo.pciInfo.u.h0.base_registers[0], B_MTR_WC);
break;
}
pciIndex++;
}
return B_OK;
}
const char** publish_devices()
{
LogFlowFunc(("publish_devices\n"));
if (gCanHasDevice)
return (const char **)gDeviceNames;
return NULL;
}
device_hooks* find_device(const char *name)
{
LogFlowFunc(("find_device\n"));
if (gCanHasDevice && strcmp(name, gDeviceInfo.name) == 0)
return &gDeviceHooks;
return NULL;
}
void uninit_driver()
{
LogFlowFunc(("uninit_driver\n"));
gLock.Delete();
put_module(VBOXGUEST_MODULE_NAME);
}
status_t device_open(const char *name, uint32 flags, void **cookie)
{
LogFlowFunc(("device_open\n"));
if (!gCanHasDevice || strcmp(name, gDeviceInfo.name) != 0)
return B_BAD_VALUE;
/* @todo init device! */
*cookie = (void *)&gDeviceInfo;
return B_OK;
}
status_t device_close(void *dev)
{
LogFlowFunc(("device_close\n"));
return B_ERROR;
}
status_t device_free(void *dev)
{
LogFlowFunc(("device_free\n"));
DeviceInfo& di = *(DeviceInfo *)dev;
gLock.Acquire();
if (di.openCount <= 1)
{
// TODO deinit device!
delete_area(di.sharedArea);
di.sharedArea = -1;
di.sharedInfo = NULL;
}
if (di.openCount > 0)
di.openCount--;
gLock.Release();
return B_OK;
}
status_t device_read(void *dev, off_t pos, void *buf, size_t *len)
{
LogFlowFunc(("device_read\n"));
return B_NOT_ALLOWED;
}
status_t device_write(void *dev, off_t pos, const void *buf, size_t *len)
{
LogFlowFunc(("device_write\n"));
return B_NOT_ALLOWED;
}
status_t device_ioctl(void *cookie, uint32 msg, void *buf, size_t len)
{
LogFlowFunc(("device_ioctl\n"));
DeviceInfo *dev = (DeviceInfo *)cookie;
switch (msg)
{
case B_GET_ACCELERANT_SIGNATURE:
{
strcpy((char *)buf, "vboxvideo.accelerant");
return B_OK;
}
case VBOXVIDEO_GET_PRIVATE_DATA:
{
/** @todo r=ramshankar: implement RTR0MemUserCopyFrom for haiku. */
return user_memcpy(buf, &dev->sharedArea, sizeof(area_id));
}
case VBOXVIDEO_GET_DEVICE_NAME:
{
/** @todo r=ramshankar: implement RTR0MemUserCopyFrom for haiku. */
if (user_strlcpy((char *)buf, gDeviceInfo.name, len) < B_OK)
return B_BAD_ADDRESS;
return B_OK;
}
case VBOXVIDEO_SET_DISPLAY_MODE:
{
display_mode *mode = (display_mode *)buf;
VBoxVideoSetModeRegisters(mode->timing.h_display, mode->timing.v_display,
mode->timing.h_display, get_depth_for_color_space(mode->space), 0, 0, 0);
gDeviceInfo.sharedInfo->currentMode = *mode;
return B_OK;
}
default:
return B_BAD_VALUE;
}
}