/* $Id: VBoxDev-haiku.c 63566 2016-08-16 14:05:58Z vboxsync $ */ /** @file * VBoxGuest kernel driver, Haiku Guest Additions, implementation. */ /* * Copyright (C) 2012-2016 Oracle 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 * François Revol * * 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 #include #include #include #include #include #include #include "VBoxGuest-haiku.h" #include "VBoxGuestInternal.h" #include #include #include #include #include #include #define DRIVER_NAME "vboxdev" #define DEVICE_NAME "misc/vboxguest" #define MODULE_NAME "generic/vboxguest" /********************************************************************************************************************************* * Global Variables * *********************************************************************************************************************************/ int32 api_version = B_CUR_DRIVER_API_VERSION; /** * Driver open hook. * * @param name The name of the device as returned by publish_devices. * @param flags Open flags. * @param cookie Where to store the session pointer. * * @return Haiku status code. */ static status_t vgdrvHaikuOpen(const char *name, uint32 flags, void **cookie) { int rc; PVBOXGUESTSESSION pSession; LogFlow((DRIVER_NAME ":vgdrvHaikuOpen\n")); /* * Create a new session. */ rc = VGDrvCommonCreateUserSession(&g_DevExt, &pSession); if (RT_SUCCESS(rc)) { Log((DRIVER_NAME ":vgdrvHaikuOpen success: g_DevExt=%p pSession=%p rc=%d pid=%d\n",&g_DevExt, pSession, rc,(int)RTProcSelf())); ASMAtomicIncU32(&cUsers); *cookie = pSession; return B_OK; } LogRel((DRIVER_NAME ":vgdrvHaikuOpen: failed. rc=%d\n", rc)); return RTErrConvertToErrno(rc); } /** * Driver close hook. * @param cookie The session. * * @return Haiku status code. */ static status_t vgdrvHaikuClose(void *cookie) { PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; Log(("vgdrvHaikuClose: pSession=%p\n", pSession)); /** @todo r=ramshankar: should we really be using the session spinlock here? */ RTSpinlockAcquire(g_DevExt.SessionSpinlock); /** @todo we don't know if it belongs to this session!! */ if (sState.selectSync) { //dprintf(DRIVER_NAME "close: unblocking select %p %x\n", sState.selectSync, sState.selectEvent); notify_select_event(sState.selectSync, sState.selectEvent); sState.selectEvent = (uint8_t)0; sState.selectRef = (uint32_t)0; sState.selectSync = (void *)NULL; } RTSpinlockRelease(g_DevExt.SessionSpinlock); return B_OK; } /** * Driver free hook. * @param cookie The session. * * @return Haiku status code. */ static status_t vgdrvHaikuFree(void *cookie) { PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; Log(("vgdrvHaikuFree: pSession=%p\n", pSession)); /* * Close the session if it's still hanging on to the device... */ if (VALID_PTR(pSession)) { VGDrvCommonCloseSession(&g_DevExt, pSession); ASMAtomicDecU32(&cUsers); } else Log(("vgdrvHaikuFree: si_drv1=%p!\n", pSession)); return B_OK; } /** * Driver IOCtl entry. * @param cookie The session. * @param op The operation to perform. * @param data The data associated with the operation. * @param len Size of the data in bytes. * * @return Haiku status code. */ static status_t vgdrvHaikuIOCtl(void *cookie, uint32 op, void *data, size_t len) { PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; int rc; Log(("vgdrvHaikuIOCtl: cookie=%p op=0x%08x data=%p len=%lu)\n", cookie, op, data, len)); /* * Validate the input. */ if (RT_UNLIKELY(!VALID_PTR(pSession))) return EINVAL; /* * Validate the request wrapper. */ #if 0 if (IOCPARM_LEN(ulCmd) != sizeof(VBGLBIGREQ)) { Log((DRIVER_NAME ": vgdrvHaikuIOCtl: bad request %lu size=%lu expected=%d\n", ulCmd, IOCPARM_LEN(ulCmd), sizeof(VBGLBIGREQ))); return ENOTTY; } #endif if (RT_UNLIKELY(len > _1M * 16)) { dprintf(DRIVER_NAME ": vgdrvHaikuIOCtl: bad size %#x; pArg=%p Cmd=%lu.\n", (unsigned)len, data, op); return EINVAL; } /* * Read the request. */ void *pvBuf = NULL; if (RT_LIKELY(len > 0)) { pvBuf = RTMemTmpAlloc(len); if (RT_UNLIKELY(!pvBuf)) { LogRel((DRIVER_NAME ":vgdrvHaikuIOCtl: RTMemTmpAlloc failed to alloc %d bytes.\n", len)); return ENOMEM; } /** @todo r=ramshankar: replace with RTR0MemUserCopyFrom() */ rc = user_memcpy(pvBuf, data, len); if (RT_UNLIKELY(rc < 0)) { RTMemTmpFree(pvBuf); LogRel((DRIVER_NAME ":vgdrvHaikuIOCtl: user_memcpy failed; pvBuf=%p data=%p op=%d. rc=%d\n", pvBuf, data, op, rc)); return EFAULT; } if (RT_UNLIKELY(!VALID_PTR(pvBuf))) { RTMemTmpFree(pvBuf); LogRel((DRIVER_NAME ":vgdrvHaikuIOCtl: pvBuf invalid pointer %p\n", pvBuf)); return EINVAL; } } Log(("vgdrvHaikuIOCtl: pSession=%p pid=%d.\n", pSession,(int)RTProcSelf())); /* * Process the IOCtl. */ size_t cbDataReturned; rc = VGDrvCommonIoCtl(op, &g_DevExt, pSession, pvBuf, len, &cbDataReturned); if (RT_SUCCESS(rc)) { rc = 0; if (RT_UNLIKELY(cbDataReturned > len)) { Log(("vgdrvHaikuIOCtl: too much output data %d expected %d\n", cbDataReturned, len)); cbDataReturned = len; } if (cbDataReturned > 0) { rc = user_memcpy(data, pvBuf, cbDataReturned); if (RT_UNLIKELY(rc < 0)) { Log(("vgdrvHaikuIOCtl: user_memcpy failed; pvBuf=%p pArg=%p Cmd=%lu. rc=%d\n", pvBuf, data, op, rc)); rc = EFAULT; } } } else { Log(("vgdrvHaikuIOCtl: VGDrvCommonIoCtl failed. rc=%d\n", rc)); rc = EFAULT; } RTMemTmpFree(pvBuf); return rc; } /** * Driver select hook. * * @param cookie The session. * @param event The event. * @param ref ??? * @param sync ??? * * @return Haiku status code. */ static status_t vgdrvHaikuSelect(void *cookie, uint8 event, uint32 ref, selectsync *sync) { PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; status_t err = B_OK; switch (event) { case B_SELECT_READ: break; default: return EINVAL; } RTSpinlockAcquire(g_DevExt.SessionSpinlock); uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq); if (pSession->u32MousePosChangedSeq != u32CurSeq) { pSession->u32MousePosChangedSeq = u32CurSeq; notify_select_event(sync, event); } else if (sState.selectSync == NULL) { sState.selectEvent = (uint8_t)event; sState.selectRef = (uint32_t)ref; sState.selectSync = (void *)sync; } else err = B_WOULD_BLOCK; RTSpinlockRelease(g_DevExt.SessionSpinlock); return err; } /** * Driver deselect hook. * @param cookie The session. * @param event The event. * @param sync ??? * * @return Haiku status code. */ static status_t vgdrvHaikuDeselect(void *cookie, uint8 event, selectsync *sync) { PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; status_t err = B_OK; //dprintf(DRIVER_NAME "deselect(,%d,%p)\n", event, sync); RTSpinlockAcquire(g_DevExt.SessionSpinlock); if (sState.selectSync == sync) { //dprintf(DRIVER_NAME "deselect: dropping: %p %x\n", sState.selectSync, sState.selectEvent); sState.selectEvent = (uint8_t)0; sState.selectRef = (uint32_t)0; sState.selectSync = NULL; } else err = B_OK; RTSpinlockRelease(g_DevExt.SessionSpinlock); return err; } /** * Driver write hook. * @param cookie The session. * @param position The offset. * @param data Pointer to the data. * @param numBytes Where to store the number of bytes written. * * @return Haiku status code. */ static status_t vgdrvHaikuWrite(void *cookie, off_t position, const void *data, size_t *numBytes) { *numBytes = 0; return B_OK; } /** * Driver read hook. * @param cookie The session. * @param position The offset. * @param data Pointer to the data. * @param numBytes Where to store the number of bytes read. * * @return Haiku status code. */ static status_t vgdrvHaikuRead(void *cookie, off_t position, void *data, size_t *numBytes) { PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; if (*numBytes == 0) return B_OK; uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq); if (pSession->u32MousePosChangedSeq != u32CurSeq) { pSession->u32MousePosChangedSeq = u32CurSeq; *numBytes = 1; return B_OK; } *numBytes = 0; return B_OK; } status_t init_hardware() { return get_module(MODULE_NAME, (module_info **)&g_VBoxGuest); } status_t init_driver() { return B_OK; } device_hooks *find_device(const char *name) { static device_hooks s_vgdrvHaikuDeviceHooks = { vgdrvHaikuOpen, vgdrvHaikuClose, vgdrvHaikuFree, vgdrvHaikuIOCtl, vgdrvHaikuRead, vgdrvHaikuWrite, vgdrvHaikuSelect, vgdrvHaikuDeselect, }; return &s_vgdrvHaikuDeviceHooks; } const char **publish_devices() { static const char *s_papszDevices[] = { DEVICE_NAME, NULL }; return s_papszDevices; } void uninit_driver() { put_module(MODULE_NAME); }