VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuest/VBoxGuest-linux.c@ 98818

Last change on this file since 98818 was 98542, checked in by vboxsync, 22 months ago

VMMDev: The host does not _use_ the full-state-mouse-protocol, it either 'supports' or 'implements' it, going with the former: s/VMMDEV_MOUSE_HOST_USES_FULL_STATE_PROTOCOL/VMMDEV_MOUSE_HOST_SUPPORTS_FULL_STATE_PROTOCOL/ bugref:10285

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 47.0 KB
Line 
1/* $Id: VBoxGuest-linux.c 98542 2023-02-10 20:59:03Z vboxsync $ */
2/** @file
3 * VBoxGuest - Linux specifics.
4 *
5 * Note. Unfortunately, the difference between this and SUPDrv-linux.c is
6 * a little bit too big to be helpful.
7 */
8
9/*
10 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
11 *
12 * This file is part of VirtualBox base platform packages, as
13 * available from https://www.virtualbox.org.
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation, in version 3 of the
18 * License.
19 *
20 * This program is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, see <https://www.gnu.org/licenses>.
27 *
28 * The contents of this file may alternatively be used under the terms
29 * of the Common Development and Distribution License Version 1.0
30 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
31 * in the VirtualBox distribution, in which case the provisions of the
32 * CDDL are applicable instead of those of the GPL.
33 *
34 * You may elect to license modified versions of this file under the
35 * terms and conditions of either the GPL or the CDDL or both.
36 *
37 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
38 */
39
40
41/*********************************************************************************************************************************
42* Header Files *
43*********************************************************************************************************************************/
44#define LOG_GROUP LOG_GROUP_SUP_DRV
45
46#include "the-linux-kernel.h"
47
48#if RTLNX_VER_MIN(2,6,15)
49# define VBOXGUEST_WITH_INPUT_DRIVER
50#endif
51
52#if RTLNX_VER_MIN(4,15,0)
53# define CONST_4_15 const
54#else
55# define CONST_4_15
56#endif
57
58#include "VBoxGuestInternal.h"
59#ifdef VBOXGUEST_WITH_INPUT_DRIVER
60# include <linux/input.h>
61#endif
62#include <linux/miscdevice.h>
63#include <linux/poll.h>
64#if RTLNX_VER_MIN(2,6,28)
65# include <linux/tty.h>
66#endif
67#include <VBox/version.h>
68#include "revision-generated.h"
69
70#include <iprt/assert.h>
71#include <iprt/asm.h>
72#include <iprt/ctype.h>
73#include <iprt/initterm.h>
74#include <iprt/mem.h>
75#include <iprt/mp.h>
76#include <iprt/process.h>
77#include <iprt/spinlock.h>
78#include <iprt/semaphore.h>
79#include <iprt/string.h>
80#include <VBox/err.h>
81#include <VBox/log.h>
82
83
84/*********************************************************************************************************************************
85* Defined Constants And Macros *
86*********************************************************************************************************************************/
87/** The device name. */
88#define DEVICE_NAME "vboxguest"
89/** The device name for the device node open to everyone. */
90#define DEVICE_NAME_USER "vboxuser"
91/** The name of the PCI driver */
92#define DRIVER_NAME DEVICE_NAME
93
94
95/* 2.4.x compatibility macros that may or may not be defined. */
96#ifndef IRQ_RETVAL
97# define irqreturn_t void
98# define IRQ_RETVAL(n)
99#endif
100
101/* uidgid.h was introduced in 3.5.0. */
102#if RTLNX_VER_MAX(3,5,0)
103# define kgid_t gid_t
104# define kuid_t uid_t
105#endif
106
107#ifdef VBOXGUEST_WITH_INPUT_DRIVER
108/** The name of input pointing device for mouse integration. */
109# define VBOX_INPUT_DEVICE_NAME "VirtualBox mouse integration"
110#endif
111
112
113/*********************************************************************************************************************************
114* Internal Functions *
115*********************************************************************************************************************************/
116static void vgdrvLinuxTermPci(struct pci_dev *pPciDev);
117static int vgdrvLinuxProbePci(struct pci_dev *pPciDev, const struct pci_device_id *id);
118static int __init vgdrvLinuxModInit(void);
119static void __exit vgdrvLinuxModExit(void);
120static int vgdrvLinuxOpen(struct inode *pInode, struct file *pFilp);
121static int vgdrvLinuxRelease(struct inode *pInode, struct file *pFilp);
122#ifdef HAVE_UNLOCKED_IOCTL
123static long vgdrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
124#else
125static int vgdrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
126#endif
127static int vgdrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg, PVBOXGUESTSESSION pSession);
128static int vgdrvLinuxFAsync(int fd, struct file *pFile, int fOn);
129static unsigned int vgdrvLinuxPoll(struct file *pFile, poll_table *pPt);
130static ssize_t vgdrvLinuxRead(struct file *pFile, char *pbBuf, size_t cbRead, loff_t *poff);
131
132
133/*********************************************************************************************************************************
134* Global Variables *
135*********************************************************************************************************************************/
136/**
137 * Device extention & session data association structure.
138 */
139static VBOXGUESTDEVEXT g_DevExt;
140/** The PCI device. */
141static struct pci_dev *g_pPciDev = NULL;
142/** The base of the I/O port range. */
143static RTIOPORT g_IOPortBase;
144/** The base of the MMIO range. */
145static RTHCPHYS g_MMIOPhysAddr = NIL_RTHCPHYS;
146/** The size of the MMIO range as seen by PCI. */
147static uint32_t g_cbMMIO;
148/** The pointer to the mapping of the MMIO range. */
149static void *g_pvMMIOBase;
150/** Wait queue used by polling. */
151static wait_queue_head_t g_PollEventQueue;
152/** Asynchronous notification stuff. */
153static struct fasync_struct *g_pFAsyncQueue;
154#ifdef VBOXGUEST_WITH_INPUT_DRIVER
155/** Pre-allocated mouse status VMMDev requests for use in the IRQ handler. */
156static VMMDevReqMouseStatusEx *g_pMouseStatusReqEx;
157#endif
158#if RTLNX_VER_MIN(2,6,0)
159/** Whether we've create the logger or not. */
160static volatile bool g_fLoggerCreated;
161/** Release logger group settings. */
162static char g_szLogGrp[128];
163/** Release logger flags settings. */
164static char g_szLogFlags[128];
165/** Release logger destination settings. */
166static char g_szLogDst[128];
167# if 0
168/** Debug logger group settings. */
169static char g_szDbgLogGrp[128];
170/** Debug logger flags settings. */
171static char g_szDbgLogFlags[128];
172/** Debug logger destination settings. */
173static char g_szDbgLogDst[128];
174# endif
175#endif
176
177/** The input device handle */
178#ifdef VBOXGUEST_WITH_INPUT_DRIVER
179static struct input_dev *g_pInputDevice = NULL;
180#endif
181
182/** The file_operations structure. */
183static struct file_operations g_FileOps =
184{
185 owner: THIS_MODULE,
186 open: vgdrvLinuxOpen,
187 release: vgdrvLinuxRelease,
188#ifdef HAVE_UNLOCKED_IOCTL
189 unlocked_ioctl: vgdrvLinuxIOCtl,
190#else
191 ioctl: vgdrvLinuxIOCtl,
192#endif
193 fasync: vgdrvLinuxFAsync,
194 read: vgdrvLinuxRead,
195 poll: vgdrvLinuxPoll,
196 llseek: no_llseek,
197};
198
199/** The miscdevice structure. */
200static struct miscdevice g_MiscDevice =
201{
202 minor: MISC_DYNAMIC_MINOR,
203 name: DEVICE_NAME,
204 fops: &g_FileOps,
205};
206
207/** The file_operations structure for the user device.
208 * @remarks For the time being we'll be using the same implementation as
209 * /dev/vboxguest here. */
210static struct file_operations g_FileOpsUser =
211{
212 owner: THIS_MODULE,
213 open: vgdrvLinuxOpen,
214 release: vgdrvLinuxRelease,
215#ifdef HAVE_UNLOCKED_IOCTL
216 unlocked_ioctl: vgdrvLinuxIOCtl,
217#else
218 ioctl: vgdrvLinuxIOCtl,
219#endif
220};
221
222/** The miscdevice structure for the user device. */
223static struct miscdevice g_MiscDeviceUser =
224{
225 minor: MISC_DYNAMIC_MINOR,
226 name: DEVICE_NAME_USER,
227 fops: &g_FileOpsUser,
228};
229
230
231/** PCI hotplug structure. */
232static const struct pci_device_id g_VBoxGuestPciId[] =
233{
234 {
235 vendor: VMMDEV_VENDORID,
236 device: VMMDEV_DEVICEID
237 },
238 {
239 /* empty entry */
240 }
241};
242
243MODULE_DEVICE_TABLE(pci, g_VBoxGuestPciId);
244
245/** Structure for registering the PCI driver. */
246static struct pci_driver g_PciDriver =
247{
248 name: DRIVER_NAME,
249 id_table: g_VBoxGuestPciId,
250 probe: vgdrvLinuxProbePci,
251 remove: vgdrvLinuxTermPci
252};
253
254#ifdef VBOXGUEST_WITH_INPUT_DRIVER
255/** Kernel IDC session to ourselves for use with the mouse events. */
256static PVBOXGUESTSESSION g_pKernelSession = NULL;
257#endif
258
259
260
261/**
262 * Converts a VBox status code to a linux error code.
263 *
264 * @returns corresponding negative linux error code.
265 * @param rc supdrv error code (SUPDRV_ERR_* defines).
266 */
267static int vgdrvLinuxConvertToNegErrno(int rc)
268{
269 if ( rc > -1000
270 && rc < 1000)
271 return -RTErrConvertToErrno(rc);
272 switch (rc)
273 {
274 case VERR_HGCM_SERVICE_NOT_FOUND: return -ESRCH;
275 case VINF_HGCM_CLIENT_REJECTED: return 0;
276 case VERR_HGCM_INVALID_CMD_ADDRESS: return -EFAULT;
277 case VINF_HGCM_ASYNC_EXECUTE: return 0;
278 case VERR_HGCM_INTERNAL: return -EPROTO;
279 case VERR_HGCM_INVALID_CLIENT_ID: return -EINVAL;
280 case VINF_HGCM_SAVE_STATE: return 0;
281 /* No reason to return this to a guest */
282 // case VERR_HGCM_SERVICE_EXISTS: return -EEXIST;
283 default:
284 AssertMsgFailed(("Unhandled error code %Rrc\n", rc));
285 return -EPROTO;
286 }
287}
288
289
290/**
291 * Does the PCI detection and init of the device.
292 *
293 * @returns 0 on success, negated errno on failure.
294 */
295static int vgdrvLinuxProbePci(struct pci_dev *pPciDev, const struct pci_device_id *id)
296{
297 int rc;
298
299 NOREF(id);
300 AssertReturn(!g_pPciDev, -EINVAL);
301 rc = pci_enable_device(pPciDev);
302 if (rc >= 0)
303 {
304 /* I/O Ports are mandatory, the MMIO bit is not. */
305 g_IOPortBase = pci_resource_start(pPciDev, 0);
306 if (g_IOPortBase != 0)
307 {
308 /*
309 * Map the register address space.
310 */
311 g_MMIOPhysAddr = pci_resource_start(pPciDev, 1);
312 g_cbMMIO = pci_resource_len(pPciDev, 1);
313 if (request_mem_region(g_MMIOPhysAddr, g_cbMMIO, DEVICE_NAME) != NULL)
314 {
315 g_pvMMIOBase = ioremap(g_MMIOPhysAddr, g_cbMMIO);
316 if (g_pvMMIOBase)
317 {
318 /** @todo why aren't we requesting ownership of the I/O ports as well? */
319 g_pPciDev = pPciDev;
320 return 0;
321 }
322
323 /* failure cleanup path */
324 LogRel((DEVICE_NAME ": ioremap failed; MMIO Addr=%RHp cb=%#x\n", g_MMIOPhysAddr, g_cbMMIO));
325 rc = -ENOMEM;
326 release_mem_region(g_MMIOPhysAddr, g_cbMMIO);
327 }
328 else
329 {
330 LogRel((DEVICE_NAME ": failed to obtain adapter memory\n"));
331 rc = -EBUSY;
332 }
333 g_MMIOPhysAddr = NIL_RTHCPHYS;
334 g_cbMMIO = 0;
335 g_IOPortBase = 0;
336 }
337 else
338 {
339 LogRel((DEVICE_NAME ": did not find expected hardware resources\n"));
340 rc = -ENXIO;
341 }
342 pci_disable_device(pPciDev);
343 }
344 else
345 LogRel((DEVICE_NAME ": could not enable device: %d\n", rc));
346 return rc;
347}
348
349
350/**
351 * Clean up the usage of the PCI device.
352 */
353static void vgdrvLinuxTermPci(struct pci_dev *pPciDev)
354{
355 g_pPciDev = NULL;
356 if (pPciDev)
357 {
358 iounmap(g_pvMMIOBase);
359 g_pvMMIOBase = NULL;
360
361 release_mem_region(g_MMIOPhysAddr, g_cbMMIO);
362 g_MMIOPhysAddr = NIL_RTHCPHYS;
363 g_cbMMIO = 0;
364
365 pci_disable_device(pPciDev);
366 }
367}
368
369
370/**
371 * Interrupt service routine.
372 *
373 * @returns In 2.4 it returns void.
374 * In 2.6 we indicate whether we've handled the IRQ or not.
375 *
376 * @param iIrq The IRQ number.
377 * @param pvDevId The device ID, a pointer to g_DevExt.
378 * @param pRegs Register set. Removed in 2.6.19.
379 */
380#if RTLNX_VER_MIN(2,6,19) && !defined(DOXYGEN_RUNNING)
381static irqreturn_t vgdrvLinuxISR(int iIrq, void *pvDevId)
382#else
383static irqreturn_t vgdrvLinuxISR(int iIrq, void *pvDevId, struct pt_regs *pRegs)
384#endif
385{
386 bool fTaken = VGDrvCommonISR(&g_DevExt);
387 return IRQ_RETVAL(fTaken);
388}
389
390
391/**
392 * Registers the ISR and initializes the poll wait queue.
393 */
394static int __init vgdrvLinuxInitISR(void)
395{
396 int rc;
397
398 init_waitqueue_head(&g_PollEventQueue);
399 rc = request_irq(g_pPciDev->irq,
400 vgdrvLinuxISR,
401#if RTLNX_VER_MIN(2,6,20)
402 IRQF_SHARED,
403#else
404 SA_SHIRQ,
405#endif
406 DEVICE_NAME,
407 &g_DevExt);
408 if (rc)
409 {
410 LogRel((DEVICE_NAME ": could not request IRQ %d: err=%d\n", g_pPciDev->irq, rc));
411 return rc;
412 }
413 return 0;
414}
415
416
417/**
418 * Deregisters the ISR.
419 */
420static void vgdrvLinuxTermISR(void)
421{
422 free_irq(g_pPciDev->irq, &g_DevExt);
423}
424
425#ifdef VBOXGUEST_WITH_INPUT_DRIVER
426
427/**
428 * Check if extended mouse pointer state request protocol is currently used by driver.
429 *
430 * @returns True if extended protocol is used, False otherwise.
431 */
432static bool vgdrvLinuxUsesMouseStatusEx(void)
433{
434 return g_pMouseStatusReqEx->Core.header.requestType == VMMDevReq_GetMouseStatusEx;
435}
436
437/**
438 * Reports the mouse integration status to the host.
439 *
440 * Calls the kernel IOCtl to report mouse status to the host on behalf of
441 * our kernel session.
442 *
443 * @param fStatus The mouse status to report.
444 */
445static int vgdrvLinuxSetMouseStatus(uint32_t fStatus)
446{
447 int rc;
448 VBGLIOCSETMOUSESTATUS Req;
449 VBGLREQHDR_INIT(&Req.Hdr, SET_MOUSE_STATUS);
450 Req.u.In.fStatus = fStatus;
451 rc = VGDrvCommonIoCtl(VBGL_IOCTL_SET_MOUSE_STATUS, &g_DevExt, g_pKernelSession, &Req.Hdr, sizeof(Req));
452 if (RT_SUCCESS(rc))
453 rc = Req.Hdr.rc;
454 return rc;
455}
456
457
458/**
459 * Called when the input device is first opened.
460 *
461 * Sets up absolute mouse reporting.
462 */
463static int vboxguestOpenInputDevice(struct input_dev *pDev)
464{
465 int rc = vgdrvLinuxSetMouseStatus( VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE
466 | VMMDEV_MOUSE_NEW_PROTOCOL
467 | (vgdrvLinuxUsesMouseStatusEx() ? VMMDEV_MOUSE_GUEST_USES_FULL_STATE_PROTOCOL : 0));
468 if (RT_FAILURE(rc))
469 return ENODEV;
470 NOREF(pDev);
471 return 0;
472}
473
474
475/**
476 * Called if all open handles to the input device are closed.
477 *
478 * Disables absolute reporting.
479 */
480static void vboxguestCloseInputDevice(struct input_dev *pDev)
481{
482 NOREF(pDev);
483 vgdrvLinuxSetMouseStatus(0);
484}
485
486
487/**
488 * Free corresponding mouse status request structure.
489 */
490static void vgdrvLinuxFreeMouseStatusReq(void)
491{
492 VbglR0GRFree(&g_pMouseStatusReqEx->Core.header);
493 g_pMouseStatusReqEx = NULL;
494}
495
496
497/**
498 * Creates the kernel input device.
499 */
500static int __init vgdrvLinuxCreateInputDevice(void)
501{
502 /* Try to allocate legacy request data first, and check if host supports extended protocol. */
503 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&g_pMouseStatusReqEx, sizeof(VMMDevReqMouseStatus), VMMDevReq_GetMouseStatus);
504 if (RT_SUCCESS(rc))
505 {
506 /* Check if host supports extended mouse state reporting. */
507 g_pMouseStatusReqEx->Core.mouseFeatures = 0;
508 rc = VbglR0GRPerform(&g_pMouseStatusReqEx->Core.header);
509 if (RT_SUCCESS(rc))
510 {
511 if (g_pMouseStatusReqEx->Core.mouseFeatures & VMMDEV_MOUSE_HOST_SUPPORTS_FULL_STATE_PROTOCOL)
512 {
513 VMMDevReqMouseStatusEx *pReqEx = NULL;
514 rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReqEx, sizeof(*pReqEx), VMMDevReq_GetMouseStatusEx);
515 if (RT_SUCCESS(rc))
516 {
517 /* De-allocate legacy request data, */
518 VbglR0GRFree(&g_pMouseStatusReqEx->Core.header);
519 /* ..and switch to extended requests. */
520 g_pMouseStatusReqEx = pReqEx;
521 LogRel(("Host supports full mouse state reporting, switching to extended mouse integration protocol\n"));
522 }
523 else
524 LogRel(("Host supports full mouse state reporting, but feature cannot be initialized, switching to legacy mouse integration protocol\n"));
525 }
526 else
527 LogRel(("Host does not support full mouse state reporting, switching to legacy mouse integration protocol\n"));
528 }
529 else
530 LogRel(("Unable to get host mouse capabilities, switching to legacy mouse integration protocol\n"));
531 }
532 else
533 rc = -ENOMEM;
534
535 if (RT_SUCCESS(rc))
536 {
537 g_pInputDevice = input_allocate_device();
538 if (g_pInputDevice)
539 {
540 g_pInputDevice->name = VBOX_INPUT_DEVICE_NAME;
541 g_pInputDevice->id.bustype = BUS_PCI;
542 g_pInputDevice->id.vendor = VMMDEV_VENDORID;
543 g_pInputDevice->id.product = VMMDEV_DEVICEID;
544 g_pInputDevice->id.version = VBOX_SHORT_VERSION;
545 g_pInputDevice->open = vboxguestOpenInputDevice;
546 g_pInputDevice->close = vboxguestCloseInputDevice;
547# if RTLNX_VER_MAX(2,6,22)
548 g_pInputDevice->cdev.dev = &g_pPciDev->dev;
549# else
550 g_pInputDevice->dev.parent = &g_pPciDev->dev;
551# endif
552 /* Set up input device capabilities. */
553 ASMBitSet(g_pInputDevice->evbit, EV_ABS);
554 ASMBitSet(g_pInputDevice->evbit, EV_KEY);
555# ifdef EV_SYN
556 ASMBitSet(g_pInputDevice->evbit, EV_SYN);
557# endif
558 ASMBitSet(g_pInputDevice->absbit, ABS_X);
559 ASMBitSet(g_pInputDevice->absbit, ABS_Y);
560
561 input_set_abs_params(g_pInputDevice, ABS_X, VMMDEV_MOUSE_RANGE_MIN, VMMDEV_MOUSE_RANGE_MAX, 0, 0);
562 input_set_abs_params(g_pInputDevice, ABS_Y, VMMDEV_MOUSE_RANGE_MIN, VMMDEV_MOUSE_RANGE_MAX, 0, 0);
563
564 ASMBitSet(g_pInputDevice->keybit, BTN_MOUSE);
565
566 /* Report extra capabilities to input layer if extended mouse state protocol
567 * will be used in communication with host. */
568 if (vgdrvLinuxUsesMouseStatusEx())
569 {
570 ASMBitSet(g_pInputDevice->evbit, EV_REL);
571 ASMBitSet(g_pInputDevice->evbit, EV_KEY);
572
573 ASMBitSet(g_pInputDevice->relbit, REL_WHEEL);
574 ASMBitSet(g_pInputDevice->relbit, REL_HWHEEL);
575
576 ASMBitSet(g_pInputDevice->keybit, BTN_LEFT);
577 ASMBitSet(g_pInputDevice->keybit, BTN_RIGHT);
578 ASMBitSet(g_pInputDevice->keybit, BTN_MIDDLE);
579 ASMBitSet(g_pInputDevice->keybit, BTN_SIDE);
580 ASMBitSet(g_pInputDevice->keybit, BTN_EXTRA);
581 }
582
583 rc = input_register_device(g_pInputDevice);
584 if (rc == 0)
585 return 0;
586
587 input_free_device(g_pInputDevice);
588 }
589 else
590 rc = -ENOMEM;
591
592 vgdrvLinuxFreeMouseStatusReq();
593 }
594
595 return rc;
596}
597
598
599/**
600 * Terminates the kernel input device.
601 */
602static void vgdrvLinuxTermInputDevice(void)
603{
604 /* Notify host that mouse integration is no longer available. */
605 vgdrvLinuxSetMouseStatus(0);
606
607 vgdrvLinuxFreeMouseStatusReq();
608
609 /* See documentation of input_register_device(): input_free_device()
610 * should not be called after a device has been registered. */
611 input_unregister_device(g_pInputDevice);
612}
613
614#endif /* VBOXGUEST_WITH_INPUT_DRIVER */
615
616/**
617 * Creates the device nodes.
618 *
619 * @returns 0 on success, negated errno on failure.
620 */
621static int __init vgdrvLinuxInitDeviceNodes(void)
622{
623 /*
624 * The full feature device node.
625 */
626 int rc = misc_register(&g_MiscDevice);
627 if (!rc)
628 {
629 /*
630 * The device node intended to be accessible by all users.
631 */
632 rc = misc_register(&g_MiscDeviceUser);
633 if (!rc)
634 return 0;
635 LogRel((DEVICE_NAME ": misc_register failed for %s (rc=%d)\n", DEVICE_NAME_USER, rc));
636 misc_deregister(&g_MiscDevice);
637 }
638 else
639 LogRel((DEVICE_NAME ": misc_register failed for %s (rc=%d)\n", DEVICE_NAME, rc));
640 return rc;
641}
642
643
644/**
645 * Deregisters the device nodes.
646 */
647static void vgdrvLinuxTermDeviceNodes(void)
648{
649 misc_deregister(&g_MiscDevice);
650 misc_deregister(&g_MiscDeviceUser);
651}
652
653
654/**
655 * Initialize module.
656 *
657 * @returns appropriate status code.
658 */
659static int __init vgdrvLinuxModInit(void)
660{
661 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
662 PRTLOGGER pRelLogger;
663 int rc;
664
665 /*
666 * Initialize IPRT first.
667 */
668 rc = RTR0Init(0);
669 if (RT_FAILURE(rc))
670 {
671 printk(KERN_ERR DEVICE_NAME ": RTR0Init failed, rc=%d.\n", rc);
672 return -EINVAL;
673 }
674
675 /*
676 * Create the release log.
677 * (We do that here instead of common code because we want to log
678 * early failures using the LogRel macro.)
679 */
680 rc = RTLogCreate(&pRelLogger, 0 /* fFlags */, "all",
681 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
682 RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER | RTLOGDEST_USER, NULL);
683 if (RT_SUCCESS(rc))
684 {
685#if RTLNX_VER_MIN(2,6,0)
686 RTLogGroupSettings(pRelLogger, g_szLogGrp);
687 RTLogFlags(pRelLogger, g_szLogFlags);
688 RTLogDestinations(pRelLogger, g_szLogDst);
689#endif
690 RTLogRelSetDefaultInstance(pRelLogger);
691 }
692#if RTLNX_VER_MIN(2,6,0)
693 g_fLoggerCreated = true;
694#endif
695
696 /*
697 * Locate and initialize the PCI device.
698 */
699 rc = pci_register_driver(&g_PciDriver);
700 if (rc >= 0 && g_pPciDev)
701 {
702 /*
703 * Call the common device extension initializer.
704 */
705#if RTLNX_VER_MIN(2,6,0) && defined(RT_ARCH_X86)
706 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux26;
707#elif RTLNX_VER_MIN(2,6,0) && defined(RT_ARCH_AMD64)
708 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux26_x64;
709#elif RTLNX_VER_MIN(2,4,0) && defined(RT_ARCH_X86)
710 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux24;
711#elif RTLNX_VER_MIN(2,4,0) && defined(RT_ARCH_AMD64)
712 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux24_x64;
713#else
714# warning "huh? which arch + version is this?"
715 VBOXOSTYPE enmOsType = VBOXOSTYPE_Linux;
716#endif
717 rc = VGDrvCommonInitDevExt(&g_DevExt,
718 g_IOPortBase,
719 g_pvMMIOBase,
720 g_cbMMIO,
721 enmOSType,
722 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
723 if (RT_SUCCESS(rc))
724 {
725 /*
726 * Register the interrupt service routine for it now that g_DevExt can handle IRQs.
727 */
728 rc = vgdrvLinuxInitISR();
729 if (rc >= 0) /** @todo r=bird: status check differs from that inside vgdrvLinuxInitISR. */
730 {
731#ifdef VBOXGUEST_WITH_INPUT_DRIVER
732 /*
733 * Create the kernel session for this driver.
734 */
735 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &g_pKernelSession);
736 if (RT_SUCCESS(rc))
737 {
738 /*
739 * Create the kernel input device.
740 */
741 rc = vgdrvLinuxCreateInputDevice();
742 if (rc >= 0)
743 {
744#endif
745 /*
746 * Read host configuration.
747 */
748 VGDrvCommonProcessOptionsFromHost(&g_DevExt);
749
750 /*
751 * Finally, create the device nodes.
752 */
753 rc = vgdrvLinuxInitDeviceNodes();
754 if (rc >= 0)
755 {
756 /* some useful information for the user but don't show this on the console */
757 LogRel((DEVICE_NAME ": Successfully loaded version " VBOX_VERSION_STRING " r" __stringify(VBOX_SVN_REV) "\n"));
758 LogRel((DEVICE_NAME ": misc device minor %d, IRQ %d, I/O port %RTiop, MMIO at %RHp (size 0x%x)\n",
759 g_MiscDevice.minor, g_pPciDev->irq, g_IOPortBase, g_MMIOPhysAddr, g_cbMMIO));
760 printk(KERN_DEBUG DEVICE_NAME ": Successfully loaded version "
761 VBOX_VERSION_STRING " r" __stringify(VBOX_SVN_REV) " (interface " RT_XSTR(VMMDEV_VERSION) ")\n");
762 return rc;
763 }
764
765 /* bail out */
766#ifdef VBOXGUEST_WITH_INPUT_DRIVER
767 vgdrvLinuxTermInputDevice();
768 }
769 else
770 {
771 LogRel((DEVICE_NAME ": vboxguestCreateInputDevice failed with rc=%Rrc\n", rc));
772 rc = RTErrConvertFromErrno(rc);
773 }
774 VGDrvCommonCloseSession(&g_DevExt, g_pKernelSession);
775 }
776#endif
777 vgdrvLinuxTermISR();
778 }
779 VGDrvCommonDeleteDevExt(&g_DevExt);
780 }
781 else
782 {
783 LogRel((DEVICE_NAME ": VGDrvCommonInitDevExt failed with rc=%Rrc\n", rc));
784 rc = RTErrConvertFromErrno(rc);
785 }
786 }
787 else
788 {
789 LogRel((DEVICE_NAME ": PCI device not found, probably running on physical hardware.\n"));
790 rc = -ENODEV;
791 }
792 pci_unregister_driver(&g_PciDriver);
793 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
794 RTLogDestroy(RTLogSetDefaultInstance(NULL));
795 RTR0Term();
796 return rc;
797}
798
799
800/**
801 * Unload the module.
802 */
803static void __exit vgdrvLinuxModExit(void)
804{
805 /*
806 * Inverse order of init.
807 */
808 vgdrvLinuxTermDeviceNodes();
809#ifdef VBOXGUEST_WITH_INPUT_DRIVER
810 vgdrvLinuxTermInputDevice();
811 VGDrvCommonCloseSession(&g_DevExt, g_pKernelSession);
812#endif
813 vgdrvLinuxTermISR();
814 VGDrvCommonDeleteDevExt(&g_DevExt);
815 pci_unregister_driver(&g_PciDriver);
816 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
817 RTLogDestroy(RTLogSetDefaultInstance(NULL));
818 RTR0Term();
819}
820
821
822/**
823 * Get the process user ID.
824 *
825 * @returns UID.
826 */
827DECLINLINE(RTUID) vgdrvLinuxGetUid(void)
828{
829#if RTLNX_VER_MIN(2,6,29)
830# if RTLNX_VER_MIN(3,5,0)
831 return from_kuid(current_user_ns(), current->cred->uid);
832# else
833 return current->cred->uid;
834# endif
835#else
836 return current->uid;
837#endif
838}
839
840
841/**
842 * Checks if the given group number is zero or not.
843 *
844 * @returns true / false.
845 * @param gid The group to check for.
846 */
847DECLINLINE(bool) vgdrvLinuxIsGroupZero(kgid_t gid)
848{
849#if RTLNX_VER_MIN(3,5,0)
850 return from_kgid(current_user_ns(), gid);
851#else
852 return gid == 0;
853#endif
854}
855
856
857/**
858 * Searches the effective group and supplementary groups for @a gid.
859 *
860 * @returns true if member, false if not.
861 * @param gid The group to check for.
862 */
863DECLINLINE(RTGID) vgdrvLinuxIsInGroupEff(kgid_t gid)
864{
865 return in_egroup_p(gid) != 0;
866}
867
868
869/**
870 * Check if we can positively or negatively determine that the process is
871 * running under a login on the physical machine console.
872 *
873 * Havne't found a good way to figure this out for graphical sessions, so this
874 * is mostly pointless. But let us try do what we can do.
875 *
876 * @returns VMMDEV_REQUESTOR_CON_XXX.
877 */
878static uint32_t vgdrvLinuxRequestorOnConsole(void)
879{
880 uint32_t fRet = VMMDEV_REQUESTOR_CON_DONT_KNOW;
881
882#if RTLNX_VER_MIN(2,6,28) /* First with tty_kref_put(). */
883 /*
884 * Check for tty0..63, ASSUMING that these are only used for the physical console.
885 */
886 struct tty_struct *pTty = get_current_tty();
887 if (pTty)
888 {
889# if RTLNX_VER_MIN(4,2,0)
890 const char *pszName = tty_name(pTty);
891# else
892 char szBuf[64];
893 const char *pszName = tty_name(pTty, szBuf);
894# endif
895 if ( pszName
896 && pszName[0] == 't'
897 && pszName[1] == 't'
898 && pszName[2] == 'y'
899 && RT_C_IS_DIGIT(pszName[3])
900 && ( pszName[4] == '\0'
901 || ( RT_C_IS_DIGIT(pszName[4])
902 && pszName[5] == '\0'
903 && (pszName[3] - '0') * 10 + (pszName[4] - '0') <= 63)) )
904 fRet = VMMDEV_REQUESTOR_CON_YES;
905 tty_kref_put(pTty);
906 }
907#endif
908
909 return fRet;
910}
911
912
913/**
914 * Device open. Called on open /dev/vboxdrv
915 *
916 * @param pInode Pointer to inode info structure.
917 * @param pFilp Associated file pointer.
918 */
919static int vgdrvLinuxOpen(struct inode *pInode, struct file *pFilp)
920{
921 int rc;
922 PVBOXGUESTSESSION pSession;
923 uint32_t fRequestor;
924 Log((DEVICE_NAME ": pFilp=%p pid=%d/%d %s\n", pFilp, RTProcSelf(), current->pid, current->comm));
925
926 /*
927 * Figure out the requestor flags.
928 * ASSUMES that the gid of /dev/vboxuser is what we should consider the special vbox group.
929 */
930 fRequestor = VMMDEV_REQUESTOR_USERMODE | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN;
931 if (vgdrvLinuxGetUid() == 0)
932 fRequestor |= VMMDEV_REQUESTOR_USR_ROOT;
933 else
934 fRequestor |= VMMDEV_REQUESTOR_USR_USER;
935 if (MINOR(pInode->i_rdev) == g_MiscDeviceUser.minor)
936 {
937 fRequestor |= VMMDEV_REQUESTOR_USER_DEVICE;
938 if (!vgdrvLinuxIsGroupZero(pInode->i_gid) && vgdrvLinuxIsInGroupEff(pInode->i_gid))
939 fRequestor |= VMMDEV_REQUESTOR_GRP_VBOX;
940 }
941 fRequestor |= vgdrvLinuxRequestorOnConsole();
942
943 /*
944 * Call common code to create the user session. Associate it with
945 * the file so we can access it in the other methods.
946 */
947 rc = VGDrvCommonCreateUserSession(&g_DevExt, fRequestor, &pSession);
948 if (RT_SUCCESS(rc))
949 pFilp->private_data = pSession;
950
951 Log(("vgdrvLinuxOpen: g_DevExt=%p pSession=%p rc=%d/%d (pid=%d/%d %s)\n",
952 &g_DevExt, pSession, rc, vgdrvLinuxConvertToNegErrno(rc), RTProcSelf(), current->pid, current->comm));
953 return vgdrvLinuxConvertToNegErrno(rc);
954}
955
956
957/**
958 * Close device.
959 *
960 * @param pInode Pointer to inode info structure.
961 * @param pFilp Associated file pointer.
962 */
963static int vgdrvLinuxRelease(struct inode *pInode, struct file *pFilp)
964{
965 Log(("vgdrvLinuxRelease: pFilp=%p pSession=%p pid=%d/%d %s\n",
966 pFilp, pFilp->private_data, RTProcSelf(), current->pid, current->comm));
967
968#if RTLNX_VER_MAX(2,6,28)
969 /* This housekeeping was needed in older kernel versions to ensure that
970 * the file pointer didn't get left on the polling queue. */
971 vgdrvLinuxFAsync(-1, pFilp, 0);
972#endif
973 VGDrvCommonCloseSession(&g_DevExt, (PVBOXGUESTSESSION)pFilp->private_data);
974 pFilp->private_data = NULL;
975 return 0;
976}
977
978
979/**
980 * Device I/O Control entry point.
981 *
982 * @param pFilp Associated file pointer.
983 * @param uCmd The function specified to ioctl().
984 * @param ulArg The argument specified to ioctl().
985 */
986#if defined(HAVE_UNLOCKED_IOCTL) || defined(DOXYGEN_RUNNING)
987static long vgdrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
988#else
989static int vgdrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
990#endif
991{
992 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFilp->private_data;
993 int rc;
994#ifndef HAVE_UNLOCKED_IOCTL
995 unlock_kernel();
996#endif
997
998#if 0 /* no fast I/O controls defined atm. */
999 if (RT_LIKELY( ( uCmd == SUP_IOCTL_FAST_DO_RAW_RUN
1000 || uCmd == SUP_IOCTL_FAST_DO_HM_RUN
1001 || uCmd == SUP_IOCTL_FAST_DO_NOP)
1002 && pSession->fUnrestricted == true))
1003 rc = VGDrvCommonIoCtlFast(uCmd, ulArg, &g_DevExt, pSession);
1004 else
1005#endif
1006 rc = vgdrvLinuxIOCtlSlow(pFilp, uCmd, ulArg, pSession);
1007
1008#ifndef HAVE_UNLOCKED_IOCTL
1009 lock_kernel();
1010#endif
1011 return rc;
1012}
1013
1014
1015/**
1016 * Device I/O Control entry point, slow variant.
1017 *
1018 * @param pFilp Associated file pointer.
1019 * @param uCmd The function specified to ioctl().
1020 * @param ulArg The argument specified to ioctl().
1021 * @param pSession The session instance.
1022 */
1023static int vgdrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg, PVBOXGUESTSESSION pSession)
1024{
1025 int rc;
1026 VBGLREQHDR Hdr;
1027 PVBGLREQHDR pHdr;
1028 uint32_t cbBuf;
1029
1030 Log6(("vgdrvLinuxIOCtlSlow: pFilp=%p uCmd=%#x ulArg=%p pid=%d/%d\n", pFilp, uCmd, (void *)ulArg, RTProcSelf(), current->pid));
1031
1032 /*
1033 * Read the header.
1034 */
1035 if (RT_FAILURE(RTR0MemUserCopyFrom(&Hdr, ulArg, sizeof(Hdr))))
1036 {
1037 Log(("vgdrvLinuxIOCtlSlow: copy_from_user(,%#lx,) failed; uCmd=%#x\n", ulArg, uCmd));
1038 return -EFAULT;
1039 }
1040 if (RT_UNLIKELY(Hdr.uVersion != VBGLREQHDR_VERSION))
1041 {
1042 Log(("vgdrvLinuxIOCtlSlow: bad header version %#x; uCmd=%#x\n", Hdr.uVersion, uCmd));
1043 return -EINVAL;
1044 }
1045
1046 /*
1047 * Buffer the request.
1048 * Note! The header is revalidated by the common code.
1049 */
1050 cbBuf = RT_MAX(Hdr.cbIn, Hdr.cbOut);
1051 if (RT_UNLIKELY(cbBuf > _1M*16))
1052 {
1053 Log(("vgdrvLinuxIOCtlSlow: too big cbBuf=%#x; uCmd=%#x\n", cbBuf, uCmd));
1054 return -E2BIG;
1055 }
1056 if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr)
1057 || (cbBuf != _IOC_SIZE(uCmd) && _IOC_SIZE(uCmd) != 0)))
1058 {
1059 Log(("vgdrvLinuxIOCtlSlow: bad ioctl cbBuf=%#x _IOC_SIZE=%#x; uCmd=%#x\n", cbBuf, _IOC_SIZE(uCmd), uCmd));
1060 return -EINVAL;
1061 }
1062 pHdr = RTMemAlloc(cbBuf);
1063 if (RT_UNLIKELY(!pHdr))
1064 {
1065 LogRel(("vgdrvLinuxIOCtlSlow: failed to allocate buffer of %d bytes for uCmd=%#x\n", cbBuf, uCmd));
1066 return -ENOMEM;
1067 }
1068 if (RT_FAILURE(RTR0MemUserCopyFrom(pHdr, ulArg, Hdr.cbIn)))
1069 {
1070 Log(("vgdrvLinuxIOCtlSlow: copy_from_user(,%#lx, %#x) failed; uCmd=%#x\n", ulArg, Hdr.cbIn, uCmd));
1071 RTMemFree(pHdr);
1072 return -EFAULT;
1073 }
1074 if (Hdr.cbIn < cbBuf)
1075 RT_BZERO((uint8_t *)pHdr + Hdr.cbIn, cbBuf - Hdr.cbIn);
1076
1077 /*
1078 * Process the IOCtl.
1079 */
1080 rc = VGDrvCommonIoCtl(uCmd, &g_DevExt, pSession, pHdr, cbBuf);
1081
1082 /*
1083 * Copy ioctl data and output buffer back to user space.
1084 */
1085 if (RT_SUCCESS(rc))
1086 {
1087 uint32_t cbOut = pHdr->cbOut;
1088 if (RT_UNLIKELY(cbOut > cbBuf))
1089 {
1090 LogRel(("vgdrvLinuxIOCtlSlow: too much output! %#x > %#x; uCmd=%#x!\n", cbOut, cbBuf, uCmd));
1091 cbOut = cbBuf;
1092 }
1093 if (RT_FAILURE(RTR0MemUserCopyTo(ulArg, pHdr, cbOut)))
1094 {
1095 /* this is really bad! */
1096 LogRel(("vgdrvLinuxIOCtlSlow: copy_to_user(%#lx,,%#x); uCmd=%#x!\n", ulArg, cbOut, uCmd));
1097 rc = -EFAULT;
1098 }
1099 }
1100 else
1101 {
1102 Log(("vgdrvLinuxIOCtlSlow: pFilp=%p uCmd=%#x ulArg=%p failed, rc=%d\n", pFilp, uCmd, (void *)ulArg, rc));
1103 rc = -EINVAL;
1104 }
1105 RTMemFree(pHdr);
1106
1107 Log6(("vgdrvLinuxIOCtlSlow: returns %d (pid=%d/%d)\n", rc, RTProcSelf(), current->pid));
1108 return rc;
1109}
1110
1111
1112/**
1113 * @note This code is duplicated on other platforms with variations, so please
1114 * keep them all up to date when making changes!
1115 */
1116int VBOXCALL VBoxGuestIDC(void *pvSession, uintptr_t uReq, PVBGLREQHDR pReqHdr, size_t cbReq)
1117{
1118 /*
1119 * Simple request validation (common code does the rest).
1120 */
1121 int rc;
1122 if ( RT_VALID_PTR(pReqHdr)
1123 && cbReq >= sizeof(*pReqHdr))
1124 {
1125 /*
1126 * All requests except the connect one requires a valid session.
1127 */
1128 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession;
1129 if (pSession)
1130 {
1131 if ( RT_VALID_PTR(pSession)
1132 && pSession->pDevExt == &g_DevExt)
1133 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
1134 else
1135 rc = VERR_INVALID_HANDLE;
1136 }
1137 else if (uReq == VBGL_IOCTL_IDC_CONNECT)
1138 {
1139 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession);
1140 if (RT_SUCCESS(rc))
1141 {
1142 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
1143 if (RT_FAILURE(rc))
1144 VGDrvCommonCloseSession(&g_DevExt, pSession);
1145 }
1146 }
1147 else
1148 rc = VERR_INVALID_HANDLE;
1149 }
1150 else
1151 rc = VERR_INVALID_POINTER;
1152 return rc;
1153}
1154EXPORT_SYMBOL(VBoxGuestIDC);
1155
1156
1157/**
1158 * Asynchronous notification activation method.
1159 *
1160 * @returns 0 on success, negative errno on failure.
1161 *
1162 * @param fd The file descriptor.
1163 * @param pFile The file structure.
1164 * @param fOn On/off indicator.
1165 */
1166static int vgdrvLinuxFAsync(int fd, struct file *pFile, int fOn)
1167{
1168 return fasync_helper(fd, pFile, fOn, &g_pFAsyncQueue);
1169}
1170
1171
1172/**
1173 * Poll function.
1174 *
1175 * This returns ready to read if the mouse pointer mode or the pointer position
1176 * has changed since last call to read.
1177 *
1178 * @returns 0 if no changes, POLLIN | POLLRDNORM if there are unseen changes.
1179 *
1180 * @param pFile The file structure.
1181 * @param pPt The poll table.
1182 *
1183 * @remarks This is probably not really used, X11 is said to use the fasync
1184 * interface instead.
1185 */
1186static unsigned int vgdrvLinuxPoll(struct file *pFile, poll_table *pPt)
1187{
1188 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFile->private_data;
1189 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
1190 unsigned int fMask = pSession->u32MousePosChangedSeq != u32CurSeq
1191 ? POLLIN | POLLRDNORM
1192 : 0;
1193 poll_wait(pFile, &g_PollEventQueue, pPt);
1194 return fMask;
1195}
1196
1197
1198/**
1199 * Read to go with our poll/fasync response.
1200 *
1201 * @returns 1 or -EINVAL.
1202 *
1203 * @param pFile The file structure.
1204 * @param pbBuf The buffer to read into.
1205 * @param cbRead The max number of bytes to read.
1206 * @param poff The current file position.
1207 *
1208 * @remarks This is probably not really used as X11 lets the driver do its own
1209 * event reading. The poll condition is therefore also cleared when we
1210 * see VMMDevReq_GetMouseStatus in vgdrvIoCtl_VMMRequest.
1211 */
1212static ssize_t vgdrvLinuxRead(struct file *pFile, char *pbBuf, size_t cbRead, loff_t *poff)
1213{
1214 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFile->private_data;
1215 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
1216
1217 if (*poff != 0)
1218 return -EINVAL;
1219
1220 /*
1221 * Fake a single byte read if we're not up to date with the current mouse position.
1222 */
1223 if ( pSession->u32MousePosChangedSeq != u32CurSeq
1224 && cbRead > 0)
1225 {
1226 pSession->u32MousePosChangedSeq = u32CurSeq;
1227 pbBuf[0] = 0;
1228 return 1;
1229 }
1230 return 0;
1231}
1232
1233
1234#ifdef VBOXGUEST_WITH_INPUT_DRIVER
1235/**
1236 * Get host mouse state.
1237 *
1238 * @returns IPRT status code.
1239 * @param pfMouseFeatures Where to store host mouse capabilities.
1240 * @param pX Where to store X axis coordinate.
1241 * @param pY Where to store Y axis coordinate.
1242 * @param pDz Where to store vertical wheel movement offset (only set if in case of VMMDevReq_GetMouseStatusEx request).
1243 * @param pDw Where to store horizontal wheel movement offset (only set if in case of VMMDevReq_GetMouseStatusEx request).
1244 * @param pfButtons Where to store mouse buttons state (only set if in case of VMMDevReq_GetMouseStatusEx request).
1245 */
1246static int vgdrvLinuxGetHostMouseState(uint32_t *pfMouseFeatures, int32_t *pX, int32_t *pY, int32_t *pDz, int32_t *pDw, uint32_t *pfButtons)
1247{
1248 int rc = VERR_INVALID_PARAMETER;
1249
1250 Assert(pfMouseFeatures);
1251 Assert(pX);
1252 Assert(pY);
1253 Assert(pDz);
1254 Assert(pDw);
1255 Assert(pfButtons);
1256
1257 /* Initialize legacy request data. */
1258 g_pMouseStatusReqEx->Core.mouseFeatures = 0;
1259 g_pMouseStatusReqEx->Core.pointerXPos = 0;
1260 g_pMouseStatusReqEx->Core.pointerYPos = 0;
1261
1262 /* Initialize extended request data if VMMDevReq_GetMouseStatusEx is used. */
1263 if (vgdrvLinuxUsesMouseStatusEx())
1264 {
1265 g_pMouseStatusReqEx->dz = 0;
1266 g_pMouseStatusReqEx->dw = 0;
1267 g_pMouseStatusReqEx->fButtons = 0;
1268 }
1269
1270 /* Get host mouse state - either lagacy or extended version. */
1271 rc = VbglR0GRPerform(&g_pMouseStatusReqEx->Core.header);
1272 if (RT_SUCCESS(rc))
1273 {
1274 *pfMouseFeatures = g_pMouseStatusReqEx->Core.mouseFeatures;
1275 *pX = g_pMouseStatusReqEx->Core.pointerXPos;
1276 *pY = g_pMouseStatusReqEx->Core.pointerYPos;
1277
1278 /* Get extended mouse state data in case of VMMDevReq_GetMouseStatusEx. */
1279 if (vgdrvLinuxUsesMouseStatusEx())
1280 {
1281 *pDz = g_pMouseStatusReqEx->dz;
1282 *pDw = g_pMouseStatusReqEx->dw;
1283 *pfButtons = g_pMouseStatusReqEx->fButtons;
1284 }
1285 }
1286
1287 return rc;
1288}
1289#endif /* VBOXGUEST_WITH_INPUT_DRIVER */
1290
1291
1292void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
1293{
1294#ifdef VBOXGUEST_WITH_INPUT_DRIVER
1295 int rc;
1296 uint32_t fMouseFeatures = 0;
1297 int32_t x = 0;
1298 int32_t y = 0;
1299 int32_t dz = 0;
1300 int32_t dw = 0;
1301 uint32_t fButtons = 0;
1302#endif
1303 NOREF(pDevExt);
1304
1305 /*
1306 * Wake up everyone that's in a poll() and post anyone that has
1307 * subscribed to async notifications.
1308 */
1309 Log3(("VGDrvNativeISRMousePollEvent: wake_up_all\n"));
1310 wake_up_all(&g_PollEventQueue);
1311 Log3(("VGDrvNativeISRMousePollEvent: kill_fasync\n"));
1312 kill_fasync(&g_pFAsyncQueue, SIGIO, POLL_IN);
1313#ifdef VBOXGUEST_WITH_INPUT_DRIVER
1314 rc = vgdrvLinuxGetHostMouseState(&fMouseFeatures, &x, &y, &dz, &dw, &fButtons);
1315 if (RT_SUCCESS(rc))
1316 {
1317 input_report_abs(g_pInputDevice, ABS_X, x);
1318 input_report_abs(g_pInputDevice, ABS_Y, y);
1319
1320 if ( vgdrvLinuxUsesMouseStatusEx()
1321 && ( fMouseFeatures
1322 & (VMMDEV_MOUSE_HOST_SUPPORTS_FULL_STATE_PROTOCOL | VMMDEV_MOUSE_GUEST_USES_FULL_STATE_PROTOCOL))
1323 == ( VMMDEV_MOUSE_HOST_SUPPORTS_FULL_STATE_PROTOCOL | VMMDEV_MOUSE_GUEST_USES_FULL_STATE_PROTOCOL ) )
1324 {
1325 /* Vertical and horizontal scroll values come as-is from GUI.
1326 * Invert values here as it is done in PS/2 mouse driver, so
1327 * scrolling direction will be exectly the same. */
1328 input_report_rel(g_pInputDevice, REL_WHEEL, -dz);
1329 input_report_rel(g_pInputDevice, REL_HWHEEL, -dw);
1330
1331 input_report_key(g_pInputDevice, BTN_LEFT, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_LEFT));
1332 input_report_key(g_pInputDevice, BTN_RIGHT, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_RIGHT));
1333 input_report_key(g_pInputDevice, BTN_MIDDLE, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_MIDDLE));
1334 input_report_key(g_pInputDevice, BTN_SIDE, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_X1));
1335 input_report_key(g_pInputDevice, BTN_EXTRA, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_X2));
1336 }
1337
1338# ifdef EV_SYN
1339 input_sync(g_pInputDevice);
1340# endif
1341 }
1342#endif
1343 Log3(("VGDrvNativeISRMousePollEvent: done\n"));
1344}
1345
1346
1347bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
1348{
1349 RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
1350 return false;
1351}
1352
1353
1354#if RTLNX_VER_MIN(2,6,0)
1355
1356/** log and dbg_log parameter setter. */
1357static int vgdrvLinuxParamLogGrpSet(const char *pszValue, CONST_4_15 struct kernel_param *pParam)
1358{
1359 if (g_fLoggerCreated)
1360 {
1361 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1362 if (pLogger)
1363 RTLogGroupSettings(pLogger, pszValue);
1364 }
1365 else if (pParam->name[0] != 'd')
1366 strlcpy(&g_szLogGrp[0], pszValue, sizeof(g_szLogGrp));
1367
1368 return 0;
1369}
1370
1371/** log and dbg_log parameter getter. */
1372static int vgdrvLinuxParamLogGrpGet(char *pszBuf, CONST_4_15 struct kernel_param *pParam)
1373{
1374 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1375 *pszBuf = '\0';
1376 if (pLogger)
1377 RTLogQueryGroupSettings(pLogger, pszBuf, _4K);
1378 return strlen(pszBuf);
1379}
1380
1381
1382/** log and dbg_log_flags parameter setter. */
1383static int vgdrvLinuxParamLogFlagsSet(const char *pszValue, CONST_4_15 struct kernel_param *pParam)
1384{
1385 if (g_fLoggerCreated)
1386 {
1387 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1388 if (pLogger)
1389 RTLogFlags(pLogger, pszValue);
1390 }
1391 else if (pParam->name[0] != 'd')
1392 strlcpy(&g_szLogFlags[0], pszValue, sizeof(g_szLogFlags));
1393 return 0;
1394}
1395
1396/** log and dbg_log_flags parameter getter. */
1397static int vgdrvLinuxParamLogFlagsGet(char *pszBuf, CONST_4_15 struct kernel_param *pParam)
1398{
1399 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1400 *pszBuf = '\0';
1401 if (pLogger)
1402 RTLogQueryFlags(pLogger, pszBuf, _4K);
1403 return strlen(pszBuf);
1404}
1405
1406
1407/** log and dbg_log_dest parameter setter. */
1408static int vgdrvLinuxParamLogDstSet(const char *pszValue, CONST_4_15 struct kernel_param *pParam)
1409{
1410 if (g_fLoggerCreated)
1411 {
1412 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1413 if (pLogger)
1414 RTLogDestinations(pLogger, pszValue);
1415 }
1416 else if (pParam->name[0] != 'd')
1417 strlcpy(&g_szLogDst[0], pszValue, sizeof(g_szLogDst));
1418 return 0;
1419}
1420
1421/** log and dbg_log_dest parameter getter. */
1422static int vgdrvLinuxParamLogDstGet(char *pszBuf, CONST_4_15 struct kernel_param *pParam)
1423{
1424 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1425 *pszBuf = '\0';
1426 if (pLogger)
1427 RTLogQueryDestinations(pLogger, pszBuf, _4K);
1428 return strlen(pszBuf);
1429}
1430
1431
1432/** r3_log_to_host parameter setter. */
1433static int vgdrvLinuxParamR3LogToHostSet(const char *pszValue, CONST_4_15 struct kernel_param *pParam)
1434{
1435 g_DevExt.fLoggingEnabled = VBDrvCommonIsOptionValueTrue(pszValue);
1436 return 0;
1437}
1438
1439/** r3_log_to_host parameter getter. */
1440static int vgdrvLinuxParamR3LogToHostGet(char *pszBuf, CONST_4_15 struct kernel_param *pParam)
1441{
1442 strcpy(pszBuf, g_DevExt.fLoggingEnabled ? "enabled" : "disabled");
1443 return strlen(pszBuf);
1444}
1445
1446
1447/*
1448 * Define module parameters.
1449 */
1450module_param_call(log, vgdrvLinuxParamLogGrpSet, vgdrvLinuxParamLogGrpGet, NULL, 0664);
1451module_param_call(log_flags, vgdrvLinuxParamLogFlagsSet, vgdrvLinuxParamLogFlagsGet, NULL, 0664);
1452module_param_call(log_dest, vgdrvLinuxParamLogDstSet, vgdrvLinuxParamLogDstGet, NULL, 0664);
1453# ifdef LOG_ENABLED
1454module_param_call(dbg_log, vgdrvLinuxParamLogGrpSet, vgdrvLinuxParamLogGrpGet, NULL, 0664);
1455module_param_call(dbg_log_flags, vgdrvLinuxParamLogFlagsSet, vgdrvLinuxParamLogFlagsGet, NULL, 0664);
1456module_param_call(dbg_log_dest, vgdrvLinuxParamLogDstSet, vgdrvLinuxParamLogDstGet, NULL, 0664);
1457# endif
1458module_param_call(r3_log_to_host, vgdrvLinuxParamR3LogToHostSet, vgdrvLinuxParamR3LogToHostGet, NULL, 0664);
1459
1460#endif /* 2.6.0 and later */
1461
1462
1463module_init(vgdrvLinuxModInit);
1464module_exit(vgdrvLinuxModExit);
1465
1466MODULE_AUTHOR(VBOX_VENDOR);
1467MODULE_DESCRIPTION(VBOX_PRODUCT " Guest Additions for Linux Module");
1468MODULE_LICENSE("GPL");
1469#ifdef MODULE_VERSION
1470MODULE_VERSION(VBOX_VERSION_STRING " r" RT_XSTR(VBOX_SVN_REV));
1471#endif
1472
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette