VBoxGuest-linux.c revision 600b4ead01ac4fbc59171df13da53c0589a3da35
/* $Rev$ */
/** @file
* VBoxGuest - Linux specifics.
*
* Note. Unfortunately, the difference between this and SUPDrv-linux.c is
* a little bit too big to be helpful.
*/
/*
* Copyright (C) 2006-2009 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
* Some lines of code to disable the local APIC on x86_64 machines taken
* from a Mandriva patch by Gwenole Beauchesne <gbeauchesne@mandriva.com>.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_SUP_DRV
#include "VBoxGuestInternal.h"
#include "the-linux-kernel.h"
#include <linux/miscdevice.h>
#include "version-generated.h"
#include <iprt/initterm.h>
#include <iprt/spinlock.h>
#include <iprt/semaphore.h>
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The device name. */
#define DEVICE_NAME "vboxguest"
/** The device name for the device node open to everyone.. */
#define DEVICE_NAME_USER "vboxuser"
# define PCI_DEV_GET(v,d,p) pci_get_device(v,d,p)
# define PCI_DEV_PUT(x) pci_dev_put(x)
#else
# define PCI_DEV_GET(v,d,p) pci_find_device(v,d,p)
# define PCI_DEV_PUT(x) do {} while(0)
#endif
/* 2.4.x compatability macros that may or may not be defined. */
#ifndef IRQ_RETVAL
# define irqreturn_t void
# define IRQ_RETVAL(n)
#endif
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static int vboxguestLinuxModInit(void);
static void vboxguestLinuxModExit(void);
#ifdef HAVE_UNLOCKED_IOCTL
#else
static int vboxguestLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
#endif
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/**
* Device extention & session data association structure.
*/
static VBOXGUESTDEVEXT g_DevExt;
/** The PCI device. */
/** The base of the I/O port range. */
static RTIOPORT g_IOPortBase;
/** The base of the MMIO range. */
/** The size of the MMIO range as seen by PCI. */
/** The pointer to the mapping of the MMIO range. */
static void *g_pvMMIOBase;
/** Wait queue used by polling. */
static wait_queue_head_t g_PollEventQueue;
/** Asynchronous notification stuff. */
static struct fasync_struct *g_pFAsyncQueue;
/** Our file node major id.
* Either set dynamically at run time or statically at compile time. */
#ifdef CONFIG_VBOXADD_MAJOR
static unsigned int g_iModuleMajor = CONFIG_VBOXADD_MAJOR;
#else
static unsigned int g_iModuleMajor = 0;
#endif
/** The file_operations structure. */
static struct file_operations g_FileOps =
{
#ifdef HAVE_UNLOCKED_IOCTL
#else
#endif
};
/** The miscdevice structure. */
static struct miscdevice g_MiscDevice =
{
};
/** The file_operations structure for the user device.
* @remarks For the time being we'll be using the same implementation as
static struct file_operations g_FileOpsUser =
{
#ifdef HAVE_UNLOCKED_IOCTL
#else
#endif
};
/** The miscdevice structure for the user device. */
static struct miscdevice g_MiscDeviceUser =
{
};
/** PCI hotplug structure. */
{
{
},
{
/* empty entry */
}
};
/**
* Converts a VBox status code to a linux error code.
*
* @returns corresponding negative linux error code.
* @param rc supdrv error code (SUPDRV_ERR_* defines).
*/
static int vboxguestLinuxConvertToNegErrno(int rc)
{
if ( rc > -1000
&& rc < 1000)
return -RTErrConvertToErrno(rc);
switch (rc)
{
case VERR_HGCM_SERVICE_NOT_FOUND: return -ESRCH;
case VINF_HGCM_CLIENT_REJECTED: return 0;
case VERR_HGCM_INVALID_CMD_ADDRESS: return -EFAULT;
case VINF_HGCM_ASYNC_EXECUTE: return 0;
case VERR_HGCM_INTERNAL: return -EPROTO;
case VERR_HGCM_INVALID_CLIENT_ID: return -EINVAL;
case VINF_HGCM_SAVE_STATE: return 0;
/* No reason to return this to a guest */
// case VERR_HGCM_SERVICE_EXISTS: return -EEXIST;
default:
return -EPROTO;
}
}
/**
* Does the PCI detection and init of the device.
*
* @returns 0 on success, negated errno on failure.
*/
static int __init vboxguestLinuxInitPci(void)
{
int rc;
if (pPciDev)
{
if (rc >= 0)
{
/* I/O Ports are mandatory, the MMIO bit is not. */
if (pci_resource_start(pPciDev, 0) != 0)
{
/*
* Map the register address space.
*/
{
if (g_DevExt.pVMMDevMemory)
{
/** @todo why aren't we requesting ownership of the I/O ports as well? */
return 0;
}
/* failure cleanup path */
}
else
{
}
g_cbMMIO = 0;
g_IOPortBase = 0;
}
else
{
}
}
else
}
else
{
}
return rc;
}
/**
* Clean up the usage of the PCI device.
*/
static void __exit vboxguestLinuxTermPci(void)
{
if (pPciDev)
{
g_pvMMIOBase = NULL;
g_cbMMIO = 0;
}
}
/**
* Interrupt service routine.
*
* @returns In 2.4 it returns void.
* In 2.6 we indicate whether we've handled the IRQ or not.
*
* @param iIrq The IRQ number.
* @param pvDevId The device ID, a pointer to g_DevExt.
* @param pvRegs Register set. Removed in 2.6.19.
*/
#else
#endif
{
/** @todo if (vboxDev->irqAckRequest->events &
* VMMDEV_EVENT_MOUSE_POSITION_CHANGED)
* kill_fasync(&vboxDev->async_queue, SIGIO, POLL_IN);
*/
return IRQ_RETVAL(fTaken);
}
/**
* Registers the ISR.
*/
static int __init vboxguestLinuxInitISR(void)
{
int rc;
#else
#endif
&g_DevExt);
if (rc)
{
return rc;
}
return 0;
}
/**
* Deregisters the ISR.
*/
static void __exit vboxguestLinuxTermISR(void)
{
}
/**
* Creates the device nodes.
*
* @returns 0 on success, negated errno on failure.
*/
static int __init vboxguestLinuxInitDeviceNodes(void)
{
int rc;
/*
* The full feature device node.
*/
if (g_iModuleMajor > 0)
{
if (rc < 0)
{
return rc;
}
}
else
{
if (rc)
{
return rc;
}
}
/*
* The device node intended to be accessible by all users.
*/
if (rc)
{
if (g_iModuleMajor > 0)
else
return rc;
}
return 0;
}
/**
* Deregisters the device nodes.
*/
static void __exit vboxguestLinuxTermDeviceNodes(void)
{
if (g_iModuleMajor > 0)
else
}
/**
* Initialize module.
*
* @returns appropriate status code.
*/
static int __init vboxguestLinuxModInit(void)
{
int rc;
/*
* Initialize IPRT first.
*/
if (RT_FAILURE(rc))
{
return -EINVAL;
}
/*
* Locate and initialize the PCI device.
*/
rc = vboxguestLinuxInitPci();
if (rc >= 0)
{
/*
* Register the interrupt service routine for it.
*/
rc = vboxguestLinuxInitISR();
if (rc >= 0)
{
/*
* Call the common device extension initializer.
*/
#else
# warning "huh? which arch + version is this?"
#endif
if (RT_SUCCESS(rc))
{
/*
* Finally, create the device nodes.
*/
if (rc >= 0)
{
/* some useful information for the user but don't show this on the console */
return rc;
}
/* bail out */
}
else
{
}
}
}
RTR0Term();
return rc;
}
/**
* Unload the module.
*/
static void __exit vboxguestLinuxModExit(void)
{
/*
* Inverse order of init.
*/
RTR0Term();
}
/**
*
* @param pInode Pointer to inode info structure.
* @param pFilp Associated file pointer.
*/
{
int rc;
/*
* Call common code to create the user session. Associate it with
* the file so we can access it in the other methods.
*/
if (RT_SUCCESS(rc))
Log(("vboxguestLinuxOpen: g_DevExt=%p pSession=%p rc=%d/%d (pid=%d/%d %s)\n",
return vboxguestLinuxConvertToNegErrno(rc);
}
/**
* Close device.
*
* @param pInode Pointer to inode info structure.
* @param pFilp Associated file pointer.
*/
{
Log(("vboxguestLinuxRelease: pFilp=%p pSession=%p pid=%d/%d %s\n",
return 0;
}
/**
* Device I/O Control entry point.
*
* @param pFilp Associated file pointer.
* @param uCmd The function specified to ioctl().
* @param ulArg The argument specified to ioctl().
*/
#ifdef HAVE_UNLOCKED_IOCTL
#else
static int vboxguestLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
#endif
{
void *pvBufFree;
void *pvBuf;
int rc;
Log6(("vboxguestLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p pid=%d/%d\n", pFilp, uCmd, (void *)ulArg, RTProcSelf(), current->pid));
/*
* Buffer the request.
*/
{
}
else
{
if (RT_UNLIKELY(!pvBuf))
{
return -ENOMEM;
}
}
{
/*
* Process the IOCtl.
*/
/*
* Copy ioctl data and output buffer back to user space.
*/
{
rc = 0;
{
}
if (cbDataReturned > 0)
{
{
}
}
}
else
{
Log(("vboxguestLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p failed, rc=%d\n", pFilp, uCmd, (void *)ulArg, rc));
}
}
else
{
}
if (pvBufFree)
return rc;
}
/**
* Asynchronous notification activation method.
*
* @returns 0 on success, negative errno on failure.
*
* @param fd The file descriptor.
* @param pFile The file structure.
*/
{
}
/**
* Poll function.
*
* This returns ready to read if the mouse pointer mode or the pointer position
* has changed since last call to read.
*
* @returns 0 if no changes, POLLIN | POLLRDNORM if there are unseen changes.
*
* @param pFile The file structure.
* @param pPt The poll table.
*
* @remarks This is probably not really used, X11 is said to use the fasync
* interface instead.
*/
{
? POLLIN | POLLRDNORM
: 0;
return fMask;
}
/**
*
* @returns 1 or -EINVAL.
*
* @param pFile The file structure.
* @param pbBuf The buffer to read into.
* @param cbRead The max number of bytes to read.
* @param poff The current file position.
*
* @remarks This is probably not really used as X11 lets the driver do its own
* event reading. The poll condition is therefore also cleared when we
* see VMMDevReq_GetMouseStatus in VBoxGuestCommonIOCtl_VMMRequest.
*/
{
if (*poff != 0)
return -EINVAL;
/*
* Fake a single byte read if we're not up to date with the current mouse position.
*/
&& cbRead > 0)
{
pbBuf[0] = 0;
return 1;
}
return 0;
}
{
/*
* Wake up everyone that's in a poll() and post anyone that has
* subscribed to async notifications.
*/
}
/* Common code that depend on g_DevExt. */
#include "VBoxGuestIDC-unix.c.h"
MODULE_AUTHOR("Sun Microsystems, Inc.");
MODULE_DESCRIPTION("VirtualBox Guest Additions for Linux Module");
MODULE_LICENSE("GPL");
#ifdef MODULE_VERSION
#endif