VirtualBox

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

Last change on this file since 97790 was 97790, checked in by vboxsync, 2 years ago

VMMDev mouse: bugref:10285: Introduce extended host mouse pointer state.

Provide VMMDev mouse with buttons and H/V wheel movement state. Extended
state can be now fetched from host in scope of VMMDevReqMouseStatusEx request.
Currently only Linux Additions utilize this functionality.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.6 KB
Line 
1/* $Id: VBoxGuest-linux.c 97790 2022-12-13 13:06: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-2022 Oracle and/or its affiliates.
11 *
12 * This file is part of VirtualBox base platform packages, as
13 * available from https://www.215389.xyz.
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
426#ifdef VBOXGUEST_WITH_INPUT_DRIVER
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 * Creates the kernel input device.
498 */
499static int __init vgdrvLinuxCreateInputDevice(void)
500{
501 /* Try to allocate legacy request data first, and check if host supports extended protocol. */
502 int rc = VbglR0GRAlloc((VMMDevRequestHeader **)&g_pMouseStatusReqEx, sizeof(VMMDevReqMouseStatus), VMMDevReq_GetMouseStatus);
503 if (RT_SUCCESS(rc))
504 {
505 /* Check if host supports extended mouse state reporting. */
506 g_pMouseStatusReqEx->Core.mouseFeatures = 0;
507 rc = VbglR0GRPerform(&g_pMouseStatusReqEx->Core.header);
508 if (RT_SUCCESS(rc))
509 {
510 if (g_pMouseStatusReqEx->Core.mouseFeatures & VMMDEV_MOUSE_HOST_USES_FULL_STATE_PROTOCOL)
511 {
512 VMMDevReqMouseStatusEx *pReqEx = NULL;
513 rc = VbglR0GRAlloc((VMMDevRequestHeader **)&pReqEx, sizeof(*pReqEx), VMMDevReq_GetMouseStatusEx);
514 if (RT_SUCCESS(rc))
515 {
516 /* De-allocate legacy request data, */
517 VbglR0GRFree(&g_pMouseStatusReqEx->Core.header);
518 /* ..and switch to extended requests. */
519 g_pMouseStatusReqEx = pReqEx;
520 LogRel(("Host supports full mouse state reporting, switching to extended mouse integration protocol\n"));
521 }
522 else
523 LogRel(("Host supports full mouse state reporting, but feature cannot be initialized, switching to legacy mouse integration protocol\n"));
524 }
525 else
526 LogRel(("Host does not support full mouse state reporting, switching to legacy mouse integration protocol\n"));
527 }
528 else
529 LogRel(("Unable to get host mouse capabilities, switching to legacy mouse integration protocol\n"));
530 }
531 else
532 rc = -ENOMEM;
533
534 if (RT_SUCCESS(rc))
535 {
536 g_pInputDevice = input_allocate_device();
537 if (g_pInputDevice)
538 {
539 g_pInputDevice->name = VBOX_INPUT_DEVICE_NAME;
540 g_pInputDevice->id.bustype = BUS_PCI;
541 g_pInputDevice->id.vendor = VMMDEV_VENDORID;
542 g_pInputDevice->id.product = VMMDEV_DEVICEID;
543 g_pInputDevice->id.version = VBOX_SHORT_VERSION;
544 g_pInputDevice->open = vboxguestOpenInputDevice;
545 g_pInputDevice->close = vboxguestCloseInputDevice;
546# if RTLNX_VER_MAX(2,6,22)
547 g_pInputDevice->cdev.dev = &g_pPciDev->dev;
548# else
549 g_pInputDevice->dev.parent = &g_pPciDev->dev;
550# endif
551 /* Set up input device capabilities. */
552 ASMBitSet(g_pInputDevice->evbit, EV_ABS);
553# ifdef EV_SYN
554 ASMBitSet(g_pInputDevice->evbit, EV_SYN);
555# endif
556 ASMBitSet(g_pInputDevice->absbit, ABS_X);
557 ASMBitSet(g_pInputDevice->absbit, ABS_Y);
558
559 input_set_abs_params(g_pInputDevice, ABS_X, VMMDEV_MOUSE_RANGE_MIN, VMMDEV_MOUSE_RANGE_MAX, 0, 0);
560 input_set_abs_params(g_pInputDevice, ABS_Y, VMMDEV_MOUSE_RANGE_MIN, VMMDEV_MOUSE_RANGE_MAX, 0, 0);
561
562 /* Report extra capabilities to input layer if extended mouse state protocol
563 * will be used in communication with host. */
564 if (vgdrvLinuxUsesMouseStatusEx())
565 {
566 ASMBitSet(g_pInputDevice->evbit, EV_REL);
567 ASMBitSet(g_pInputDevice->evbit, EV_KEY);
568
569 ASMBitSet(g_pInputDevice->relbit, REL_WHEEL);
570 ASMBitSet(g_pInputDevice->relbit, REL_HWHEEL);
571
572 ASMBitSet(g_pInputDevice->keybit, BTN_LEFT);
573 ASMBitSet(g_pInputDevice->keybit, BTN_RIGHT);
574 ASMBitSet(g_pInputDevice->keybit, BTN_MIDDLE);
575 ASMBitSet(g_pInputDevice->keybit, BTN_SIDE);
576 ASMBitSet(g_pInputDevice->keybit, BTN_EXTRA);
577 }
578
579 rc = input_register_device(g_pInputDevice);
580 if (rc == 0)
581 return 0;
582
583 input_free_device(g_pInputDevice);
584 }
585 else
586 rc = -ENOMEM;
587
588 vgdrvLinuxFreeMouseStatusReq();
589 }
590
591 return rc;
592}
593
594
595/**
596 * Terminates the kernel input device.
597 */
598static void vgdrvLinuxTermInputDevice(void)
599{
600 /* Notify host that mouse integration is no longer available. */
601 vgdrvLinuxSetMouseStatus(0);
602
603 vgdrvLinuxFreeMouseStatusReq();
604
605 /* See documentation of input_register_device(): input_free_device()
606 * should not be called after a device has been registered. */
607 input_unregister_device(g_pInputDevice);
608}
609
610#endif /* VBOXGUEST_WITH_INPUT_DRIVER */
611
612/**
613 * Creates the device nodes.
614 *
615 * @returns 0 on success, negated errno on failure.
616 */
617static int __init vgdrvLinuxInitDeviceNodes(void)
618{
619 /*
620 * The full feature device node.
621 */
622 int rc = misc_register(&g_MiscDevice);
623 if (!rc)
624 {
625 /*
626 * The device node intended to be accessible by all users.
627 */
628 rc = misc_register(&g_MiscDeviceUser);
629 if (!rc)
630 return 0;
631 LogRel((DEVICE_NAME ": misc_register failed for %s (rc=%d)\n", DEVICE_NAME_USER, rc));
632 misc_deregister(&g_MiscDevice);
633 }
634 else
635 LogRel((DEVICE_NAME ": misc_register failed for %s (rc=%d)\n", DEVICE_NAME, rc));
636 return rc;
637}
638
639
640/**
641 * Deregisters the device nodes.
642 */
643static void vgdrvLinuxTermDeviceNodes(void)
644{
645 misc_deregister(&g_MiscDevice);
646 misc_deregister(&g_MiscDeviceUser);
647}
648
649
650/**
651 * Initialize module.
652 *
653 * @returns appropriate status code.
654 */
655static int __init vgdrvLinuxModInit(void)
656{
657 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
658 PRTLOGGER pRelLogger;
659 int rc;
660
661 /*
662 * Initialize IPRT first.
663 */
664 rc = RTR0Init(0);
665 if (RT_FAILURE(rc))
666 {
667 printk(KERN_ERR DEVICE_NAME ": RTR0Init failed, rc=%d.\n", rc);
668 return -EINVAL;
669 }
670
671 /*
672 * Create the release log.
673 * (We do that here instead of common code because we want to log
674 * early failures using the LogRel macro.)
675 */
676 rc = RTLogCreate(&pRelLogger, 0 /* fFlags */, "all",
677 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
678 RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER | RTLOGDEST_USER, NULL);
679 if (RT_SUCCESS(rc))
680 {
681#if RTLNX_VER_MIN(2,6,0)
682 RTLogGroupSettings(pRelLogger, g_szLogGrp);
683 RTLogFlags(pRelLogger, g_szLogFlags);
684 RTLogDestinations(pRelLogger, g_szLogDst);
685#endif
686 RTLogRelSetDefaultInstance(pRelLogger);
687 }
688#if RTLNX_VER_MIN(2,6,0)
689 g_fLoggerCreated = true;
690#endif
691
692 /*
693 * Locate and initialize the PCI device.
694 */
695 rc = pci_register_driver(&g_PciDriver);
696 if (rc >= 0 && g_pPciDev)
697 {
698 /*
699 * Call the common device extension initializer.
700 */
701#if RTLNX_VER_MIN(2,6,0) && defined(RT_ARCH_X86)
702 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux26;
703#elif RTLNX_VER_MIN(2,6,0) && defined(RT_ARCH_AMD64)
704 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux26_x64;
705#elif RTLNX_VER_MIN(2,4,0) && defined(RT_ARCH_X86)
706 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux24;
707#elif RTLNX_VER_MIN(2,4,0) && defined(RT_ARCH_AMD64)
708 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux24_x64;
709#else
710# warning "huh? which arch + version is this?"
711 VBOXOSTYPE enmOsType = VBOXOSTYPE_Linux;
712#endif
713 rc = VGDrvCommonInitDevExt(&g_DevExt,
714 g_IOPortBase,
715 g_pvMMIOBase,
716 g_cbMMIO,
717 enmOSType,
718 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
719 if (RT_SUCCESS(rc))
720 {
721 /*
722 * Register the interrupt service routine for it now that g_DevExt can handle IRQs.
723 */
724 rc = vgdrvLinuxInitISR();
725 if (rc >= 0) /** @todo r=bird: status check differs from that inside vgdrvLinuxInitISR. */
726 {
727#ifdef VBOXGUEST_WITH_INPUT_DRIVER
728 /*
729 * Create the kernel session for this driver.
730 */
731 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &g_pKernelSession);
732 if (RT_SUCCESS(rc))
733 {
734 /*
735 * Create the kernel input device.
736 */
737 rc = vgdrvLinuxCreateInputDevice();
738 if (rc >= 0)
739 {
740#endif
741 /*
742 * Read host configuration.
743 */
744 VGDrvCommonProcessOptionsFromHost(&g_DevExt);
745
746 /*
747 * Finally, create the device nodes.
748 */
749 rc = vgdrvLinuxInitDeviceNodes();
750 if (rc >= 0)
751 {
752 /* some useful information for the user but don't show this on the console */
753 LogRel((DEVICE_NAME ": Successfully loaded version " VBOX_VERSION_STRING " r" __stringify(VBOX_SVN_REV) "\n"));
754 LogRel((DEVICE_NAME ": misc device minor %d, IRQ %d, I/O port %RTiop, MMIO at %RHp (size 0x%x)\n",
755 g_MiscDevice.minor, g_pPciDev->irq, g_IOPortBase, g_MMIOPhysAddr, g_cbMMIO));
756 printk(KERN_DEBUG DEVICE_NAME ": Successfully loaded version "
757 VBOX_VERSION_STRING " r" __stringify(VBOX_SVN_REV) " (interface " RT_XSTR(VMMDEV_VERSION) ")\n");
758 return rc;
759 }
760
761 /* bail out */
762#ifdef VBOXGUEST_WITH_INPUT_DRIVER
763 vgdrvLinuxTermInputDevice();
764 }
765 else
766 {
767 LogRel((DEVICE_NAME ": vboxguestCreateInputDevice failed with rc=%Rrc\n", rc));
768 rc = RTErrConvertFromErrno(rc);
769 }
770 VGDrvCommonCloseSession(&g_DevExt, g_pKernelSession);
771 }
772#endif
773 vgdrvLinuxTermISR();
774 }
775 VGDrvCommonDeleteDevExt(&g_DevExt);
776 }
777 else
778 {
779 LogRel((DEVICE_NAME ": VGDrvCommonInitDevExt failed with rc=%Rrc\n", rc));
780 rc = RTErrConvertFromErrno(rc);
781 }
782 }
783 else
784 {
785 LogRel((DEVICE_NAME ": PCI device not found, probably running on physical hardware.\n"));
786 rc = -ENODEV;
787 }
788 pci_unregister_driver(&g_PciDriver);
789 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
790 RTLogDestroy(RTLogSetDefaultInstance(NULL));
791 RTR0Term();
792 return rc;
793}
794
795
796/**
797 * Unload the module.
798 */
799static void __exit vgdrvLinuxModExit(void)
800{
801 /*
802 * Inverse order of init.
803 */
804 vgdrvLinuxTermDeviceNodes();
805#ifdef VBOXGUEST_WITH_INPUT_DRIVER
806 vgdrvLinuxTermInputDevice();
807 VGDrvCommonCloseSession(&g_DevExt, g_pKernelSession);
808#endif
809 vgdrvLinuxTermISR();
810 VGDrvCommonDeleteDevExt(&g_DevExt);
811 pci_unregister_driver(&g_PciDriver);
812 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
813 RTLogDestroy(RTLogSetDefaultInstance(NULL));
814 RTR0Term();
815}
816
817
818/**
819 * Get the process user ID.
820 *
821 * @returns UID.
822 */
823DECLINLINE(RTUID) vgdrvLinuxGetUid(void)
824{
825#if RTLNX_VER_MIN(2,6,29)
826# if RTLNX_VER_MIN(3,5,0)
827 return from_kuid(current_user_ns(), current->cred->uid);
828# else
829 return current->cred->uid;
830# endif
831#else
832 return current->uid;
833#endif
834}
835
836
837/**
838 * Checks if the given group number is zero or not.
839 *
840 * @returns true / false.
841 * @param gid The group to check for.
842 */
843DECLINLINE(bool) vgdrvLinuxIsGroupZero(kgid_t gid)
844{
845#if RTLNX_VER_MIN(3,5,0)
846 return from_kgid(current_user_ns(), gid);
847#else
848 return gid == 0;
849#endif
850}
851
852
853/**
854 * Searches the effective group and supplementary groups for @a gid.
855 *
856 * @returns true if member, false if not.
857 * @param gid The group to check for.
858 */
859DECLINLINE(RTGID) vgdrvLinuxIsInGroupEff(kgid_t gid)
860{
861 return in_egroup_p(gid) != 0;
862}
863
864
865/**
866 * Check if we can positively or negatively determine that the process is
867 * running under a login on the physical machine console.
868 *
869 * Havne't found a good way to figure this out for graphical sessions, so this
870 * is mostly pointless. But let us try do what we can do.
871 *
872 * @returns VMMDEV_REQUESTOR_CON_XXX.
873 */
874static uint32_t vgdrvLinuxRequestorOnConsole(void)
875{
876 uint32_t fRet = VMMDEV_REQUESTOR_CON_DONT_KNOW;
877
878#if RTLNX_VER_MIN(2,6,28) /* First with tty_kref_put(). */
879 /*
880 * Check for tty0..63, ASSUMING that these are only used for the physical console.
881 */
882 struct tty_struct *pTty = get_current_tty();
883 if (pTty)
884 {
885# if RTLNX_VER_MIN(4,2,0)
886 const char *pszName = tty_name(pTty);
887# else
888 char szBuf[64];
889 const char *pszName = tty_name(pTty, szBuf);
890# endif
891 if ( pszName
892 && pszName[0] == 't'
893 && pszName[1] == 't'
894 && pszName[2] == 'y'
895 && RT_C_IS_DIGIT(pszName[3])
896 && ( pszName[4] == '\0'
897 || ( RT_C_IS_DIGIT(pszName[4])
898 && pszName[5] == '\0'
899 && (pszName[3] - '0') * 10 + (pszName[4] - '0') <= 63)) )
900 fRet = VMMDEV_REQUESTOR_CON_YES;
901 tty_kref_put(pTty);
902 }
903#endif
904
905 return fRet;
906}
907
908
909/**
910 * Device open. Called on open /dev/vboxdrv
911 *
912 * @param pInode Pointer to inode info structure.
913 * @param pFilp Associated file pointer.
914 */
915static int vgdrvLinuxOpen(struct inode *pInode, struct file *pFilp)
916{
917 int rc;
918 PVBOXGUESTSESSION pSession;
919 uint32_t fRequestor;
920 Log((DEVICE_NAME ": pFilp=%p pid=%d/%d %s\n", pFilp, RTProcSelf(), current->pid, current->comm));
921
922 /*
923 * Figure out the requestor flags.
924 * ASSUMES that the gid of /dev/vboxuser is what we should consider the special vbox group.
925 */
926 fRequestor = VMMDEV_REQUESTOR_USERMODE | VMMDEV_REQUESTOR_TRUST_NOT_GIVEN;
927 if (vgdrvLinuxGetUid() == 0)
928 fRequestor |= VMMDEV_REQUESTOR_USR_ROOT;
929 else
930 fRequestor |= VMMDEV_REQUESTOR_USR_USER;
931 if (MINOR(pInode->i_rdev) == g_MiscDeviceUser.minor)
932 {
933 fRequestor |= VMMDEV_REQUESTOR_USER_DEVICE;
934 if (!vgdrvLinuxIsGroupZero(pInode->i_gid) && vgdrvLinuxIsInGroupEff(pInode->i_gid))
935 fRequestor |= VMMDEV_REQUESTOR_GRP_VBOX;
936 }
937 fRequestor |= vgdrvLinuxRequestorOnConsole();
938
939 /*
940 * Call common code to create the user session. Associate it with
941 * the file so we can access it in the other methods.
942 */
943 rc = VGDrvCommonCreateUserSession(&g_DevExt, fRequestor, &pSession);
944 if (RT_SUCCESS(rc))
945 pFilp->private_data = pSession;
946
947 Log(("vgdrvLinuxOpen: g_DevExt=%p pSession=%p rc=%d/%d (pid=%d/%d %s)\n",
948 &g_DevExt, pSession, rc, vgdrvLinuxConvertToNegErrno(rc), RTProcSelf(), current->pid, current->comm));
949 return vgdrvLinuxConvertToNegErrno(rc);
950}
951
952
953/**
954 * Close device.
955 *
956 * @param pInode Pointer to inode info structure.
957 * @param pFilp Associated file pointer.
958 */
959static int vgdrvLinuxRelease(struct inode *pInode, struct file *pFilp)
960{
961 Log(("vgdrvLinuxRelease: pFilp=%p pSession=%p pid=%d/%d %s\n",
962 pFilp, pFilp->private_data, RTProcSelf(), current->pid, current->comm));
963
964#if RTLNX_VER_MAX(2,6,28)
965 /* This housekeeping was needed in older kernel versions to ensure that
966 * the file pointer didn't get left on the polling queue. */
967 vgdrvLinuxFAsync(-1, pFilp, 0);
968#endif
969 VGDrvCommonCloseSession(&g_DevExt, (PVBOXGUESTSESSION)pFilp->private_data);
970 pFilp->private_data = NULL;
971 return 0;
972}
973
974
975/**
976 * Device I/O Control entry point.
977 *
978 * @param pFilp Associated file pointer.
979 * @param uCmd The function specified to ioctl().
980 * @param ulArg The argument specified to ioctl().
981 */
982#if defined(HAVE_UNLOCKED_IOCTL) || defined(DOXYGEN_RUNNING)
983static long vgdrvLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
984#else
985static int vgdrvLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
986#endif
987{
988 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFilp->private_data;
989 int rc;
990#ifndef HAVE_UNLOCKED_IOCTL
991 unlock_kernel();
992#endif
993
994#if 0 /* no fast I/O controls defined atm. */
995 if (RT_LIKELY( ( uCmd == SUP_IOCTL_FAST_DO_RAW_RUN
996 || uCmd == SUP_IOCTL_FAST_DO_HM_RUN
997 || uCmd == SUP_IOCTL_FAST_DO_NOP)
998 && pSession->fUnrestricted == true))
999 rc = VGDrvCommonIoCtlFast(uCmd, ulArg, &g_DevExt, pSession);
1000 else
1001#endif
1002 rc = vgdrvLinuxIOCtlSlow(pFilp, uCmd, ulArg, pSession);
1003
1004#ifndef HAVE_UNLOCKED_IOCTL
1005 lock_kernel();
1006#endif
1007 return rc;
1008}
1009
1010
1011/**
1012 * Device I/O Control entry point, slow variant.
1013 *
1014 * @param pFilp Associated file pointer.
1015 * @param uCmd The function specified to ioctl().
1016 * @param ulArg The argument specified to ioctl().
1017 * @param pSession The session instance.
1018 */
1019static int vgdrvLinuxIOCtlSlow(struct file *pFilp, unsigned int uCmd, unsigned long ulArg, PVBOXGUESTSESSION pSession)
1020{
1021 int rc;
1022 VBGLREQHDR Hdr;
1023 PVBGLREQHDR pHdr;
1024 uint32_t cbBuf;
1025
1026 Log6(("vgdrvLinuxIOCtlSlow: pFilp=%p uCmd=%#x ulArg=%p pid=%d/%d\n", pFilp, uCmd, (void *)ulArg, RTProcSelf(), current->pid));
1027
1028 /*
1029 * Read the header.
1030 */
1031 if (RT_FAILURE(RTR0MemUserCopyFrom(&Hdr, ulArg, sizeof(Hdr))))
1032 {
1033 Log(("vgdrvLinuxIOCtlSlow: copy_from_user(,%#lx,) failed; uCmd=%#x\n", ulArg, uCmd));
1034 return -EFAULT;
1035 }
1036 if (RT_UNLIKELY(Hdr.uVersion != VBGLREQHDR_VERSION))
1037 {
1038 Log(("vgdrvLinuxIOCtlSlow: bad header version %#x; uCmd=%#x\n", Hdr.uVersion, uCmd));
1039 return -EINVAL;
1040 }
1041
1042 /*
1043 * Buffer the request.
1044 * Note! The header is revalidated by the common code.
1045 */
1046 cbBuf = RT_MAX(Hdr.cbIn, Hdr.cbOut);
1047 if (RT_UNLIKELY(cbBuf > _1M*16))
1048 {
1049 Log(("vgdrvLinuxIOCtlSlow: too big cbBuf=%#x; uCmd=%#x\n", cbBuf, uCmd));
1050 return -E2BIG;
1051 }
1052 if (RT_UNLIKELY( Hdr.cbIn < sizeof(Hdr)
1053 || (cbBuf != _IOC_SIZE(uCmd) && _IOC_SIZE(uCmd) != 0)))
1054 {
1055 Log(("vgdrvLinuxIOCtlSlow: bad ioctl cbBuf=%#x _IOC_SIZE=%#x; uCmd=%#x\n", cbBuf, _IOC_SIZE(uCmd), uCmd));
1056 return -EINVAL;
1057 }
1058 pHdr = RTMemAlloc(cbBuf);
1059 if (RT_UNLIKELY(!pHdr))
1060 {
1061 LogRel(("vgdrvLinuxIOCtlSlow: failed to allocate buffer of %d bytes for uCmd=%#x\n", cbBuf, uCmd));
1062 return -ENOMEM;
1063 }
1064 if (RT_FAILURE(RTR0MemUserCopyFrom(pHdr, ulArg, Hdr.cbIn)))
1065 {
1066 Log(("vgdrvLinuxIOCtlSlow: copy_from_user(,%#lx, %#x) failed; uCmd=%#x\n", ulArg, Hdr.cbIn, uCmd));
1067 RTMemFree(pHdr);
1068 return -EFAULT;
1069 }
1070 if (Hdr.cbIn < cbBuf)
1071 RT_BZERO((uint8_t *)pHdr + Hdr.cbIn, cbBuf - Hdr.cbIn);
1072
1073 /*
1074 * Process the IOCtl.
1075 */
1076 rc = VGDrvCommonIoCtl(uCmd, &g_DevExt, pSession, pHdr, cbBuf);
1077
1078 /*
1079 * Copy ioctl data and output buffer back to user space.
1080 */
1081 if (RT_SUCCESS(rc))
1082 {
1083 uint32_t cbOut = pHdr->cbOut;
1084 if (RT_UNLIKELY(cbOut > cbBuf))
1085 {
1086 LogRel(("vgdrvLinuxIOCtlSlow: too much output! %#x > %#x; uCmd=%#x!\n", cbOut, cbBuf, uCmd));
1087 cbOut = cbBuf;
1088 }
1089 if (RT_FAILURE(RTR0MemUserCopyTo(ulArg, pHdr, cbOut)))
1090 {
1091 /* this is really bad! */
1092 LogRel(("vgdrvLinuxIOCtlSlow: copy_to_user(%#lx,,%#x); uCmd=%#x!\n", ulArg, cbOut, uCmd));
1093 rc = -EFAULT;
1094 }
1095 }
1096 else
1097 {
1098 Log(("vgdrvLinuxIOCtlSlow: pFilp=%p uCmd=%#x ulArg=%p failed, rc=%d\n", pFilp, uCmd, (void *)ulArg, rc));
1099 rc = -EINVAL;
1100 }
1101 RTMemFree(pHdr);
1102
1103 Log6(("vgdrvLinuxIOCtlSlow: returns %d (pid=%d/%d)\n", rc, RTProcSelf(), current->pid));
1104 return rc;
1105}
1106
1107
1108/**
1109 * @note This code is duplicated on other platforms with variations, so please
1110 * keep them all up to date when making changes!
1111 */
1112int VBOXCALL VBoxGuestIDC(void *pvSession, uintptr_t uReq, PVBGLREQHDR pReqHdr, size_t cbReq)
1113{
1114 /*
1115 * Simple request validation (common code does the rest).
1116 */
1117 int rc;
1118 if ( RT_VALID_PTR(pReqHdr)
1119 && cbReq >= sizeof(*pReqHdr))
1120 {
1121 /*
1122 * All requests except the connect one requires a valid session.
1123 */
1124 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession;
1125 if (pSession)
1126 {
1127 if ( RT_VALID_PTR(pSession)
1128 && pSession->pDevExt == &g_DevExt)
1129 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
1130 else
1131 rc = VERR_INVALID_HANDLE;
1132 }
1133 else if (uReq == VBGL_IOCTL_IDC_CONNECT)
1134 {
1135 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &pSession);
1136 if (RT_SUCCESS(rc))
1137 {
1138 rc = VGDrvCommonIoCtl(uReq, &g_DevExt, pSession, pReqHdr, cbReq);
1139 if (RT_FAILURE(rc))
1140 VGDrvCommonCloseSession(&g_DevExt, pSession);
1141 }
1142 }
1143 else
1144 rc = VERR_INVALID_HANDLE;
1145 }
1146 else
1147 rc = VERR_INVALID_POINTER;
1148 return rc;
1149}
1150EXPORT_SYMBOL(VBoxGuestIDC);
1151
1152
1153/**
1154 * Asynchronous notification activation method.
1155 *
1156 * @returns 0 on success, negative errno on failure.
1157 *
1158 * @param fd The file descriptor.
1159 * @param pFile The file structure.
1160 * @param fOn On/off indicator.
1161 */
1162static int vgdrvLinuxFAsync(int fd, struct file *pFile, int fOn)
1163{
1164 return fasync_helper(fd, pFile, fOn, &g_pFAsyncQueue);
1165}
1166
1167
1168/**
1169 * Poll function.
1170 *
1171 * This returns ready to read if the mouse pointer mode or the pointer position
1172 * has changed since last call to read.
1173 *
1174 * @returns 0 if no changes, POLLIN | POLLRDNORM if there are unseen changes.
1175 *
1176 * @param pFile The file structure.
1177 * @param pPt The poll table.
1178 *
1179 * @remarks This is probably not really used, X11 is said to use the fasync
1180 * interface instead.
1181 */
1182static unsigned int vgdrvLinuxPoll(struct file *pFile, poll_table *pPt)
1183{
1184 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFile->private_data;
1185 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
1186 unsigned int fMask = pSession->u32MousePosChangedSeq != u32CurSeq
1187 ? POLLIN | POLLRDNORM
1188 : 0;
1189 poll_wait(pFile, &g_PollEventQueue, pPt);
1190 return fMask;
1191}
1192
1193
1194/**
1195 * Read to go with our poll/fasync response.
1196 *
1197 * @returns 1 or -EINVAL.
1198 *
1199 * @param pFile The file structure.
1200 * @param pbBuf The buffer to read into.
1201 * @param cbRead The max number of bytes to read.
1202 * @param poff The current file position.
1203 *
1204 * @remarks This is probably not really used as X11 lets the driver do its own
1205 * event reading. The poll condition is therefore also cleared when we
1206 * see VMMDevReq_GetMouseStatus in vgdrvIoCtl_VMMRequest.
1207 */
1208static ssize_t vgdrvLinuxRead(struct file *pFile, char *pbBuf, size_t cbRead, loff_t *poff)
1209{
1210 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFile->private_data;
1211 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
1212
1213 if (*poff != 0)
1214 return -EINVAL;
1215
1216 /*
1217 * Fake a single byte read if we're not up to date with the current mouse position.
1218 */
1219 if ( pSession->u32MousePosChangedSeq != u32CurSeq
1220 && cbRead > 0)
1221 {
1222 pSession->u32MousePosChangedSeq = u32CurSeq;
1223 pbBuf[0] = 0;
1224 return 1;
1225 }
1226 return 0;
1227}
1228
1229
1230#ifdef VBOXGUEST_WITH_INPUT_DRIVER
1231/**
1232 * Get host mouse state.
1233 *
1234 * @returns IPRT status code.
1235 * @param pfMouseFeatures Where to store host mouse capabilities.
1236 * @param pX Where to store X axis coordinate.
1237 * @param pY Where to store Y axis coordinate.
1238 * @param pDz Where to store vertical wheel movement offset (only set if in case of VMMDevReq_GetMouseStatusEx request).
1239 * @param pDw Where to store horizontal wheel movement offset (only set if in case of VMMDevReq_GetMouseStatusEx request).
1240 * @param pfButtons Where to store mouse buttons state (only set if in case of VMMDevReq_GetMouseStatusEx request).
1241 */
1242static int vgdrvLinuxGetHostMouseState(uint32_t *pfMouseFeatures, int32_t *pX, int32_t *pY, int32_t *pDz, int32_t *pDw, uint32_t *pfButtons)
1243{
1244 int rc = VERR_INVALID_PARAMETER;
1245
1246 Assert(pfMouseFeatures);
1247 Assert(pX);
1248 Assert(pY);
1249 Assert(pDz);
1250 Assert(pDw);
1251 Assert(pfButtons);
1252
1253 /* Initialize legacy request data. */
1254 g_pMouseStatusReqEx->Core.mouseFeatures = 0;
1255 g_pMouseStatusReqEx->Core.pointerXPos = 0;
1256 g_pMouseStatusReqEx->Core.pointerYPos = 0;
1257
1258 /* Initialize extended request data if VMMDevReq_GetMouseStatusEx is used. */
1259 if (vgdrvLinuxUsesMouseStatusEx())
1260 {
1261 g_pMouseStatusReqEx->dz = 0;
1262 g_pMouseStatusReqEx->dw = 0;
1263 g_pMouseStatusReqEx->fButtons = 0;
1264 }
1265
1266 /* Get host mouse state - either lagacy or extended version. */
1267 rc = VbglR0GRPerform(&g_pMouseStatusReqEx->Core.header);
1268 if (RT_SUCCESS(rc))
1269 {
1270 *pfMouseFeatures = g_pMouseStatusReqEx->Core.mouseFeatures;
1271 *pX = g_pMouseStatusReqEx->Core.pointerXPos;
1272 *pY = g_pMouseStatusReqEx->Core.pointerYPos;
1273
1274 /* Get extended mouse state data in case of VMMDevReq_GetMouseStatusEx. */
1275 if (vgdrvLinuxUsesMouseStatusEx())
1276 {
1277 *pDz = g_pMouseStatusReqEx->dz;
1278 *pDw = g_pMouseStatusReqEx->dw;
1279 *pfButtons = g_pMouseStatusReqEx->fButtons;
1280 }
1281 }
1282
1283 return rc;
1284}
1285#endif /* VBOXGUEST_WITH_INPUT_DRIVER */
1286
1287
1288void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
1289{
1290#ifdef VBOXGUEST_WITH_INPUT_DRIVER
1291 int rc;
1292 uint32_t fMouseFeatures = 0;
1293 int32_t x = 0;
1294 int32_t y = 0;
1295 int32_t dz = 0;
1296 int32_t dw = 0;
1297 uint32_t fButtons = 0;
1298#endif
1299 NOREF(pDevExt);
1300
1301 /*
1302 * Wake up everyone that's in a poll() and post anyone that has
1303 * subscribed to async notifications.
1304 */
1305 Log3(("VGDrvNativeISRMousePollEvent: wake_up_all\n"));
1306 wake_up_all(&g_PollEventQueue);
1307 Log3(("VGDrvNativeISRMousePollEvent: kill_fasync\n"));
1308 kill_fasync(&g_pFAsyncQueue, SIGIO, POLL_IN);
1309#ifdef VBOXGUEST_WITH_INPUT_DRIVER
1310 rc = vgdrvLinuxGetHostMouseState(&fMouseFeatures, &x, &y, &dz, &dw, &fButtons);
1311 if (RT_SUCCESS(rc))
1312 {
1313 input_report_abs(g_pInputDevice, ABS_X, x);
1314 input_report_abs(g_pInputDevice, ABS_Y, y);
1315
1316 if ( vgdrvLinuxUsesMouseStatusEx()
1317 && fMouseFeatures & VMMDEV_MOUSE_HOST_USES_FULL_STATE_PROTOCOL
1318 && fMouseFeatures & VMMDEV_MOUSE_GUEST_USES_FULL_STATE_PROTOCOL)
1319 {
1320 input_report_rel(g_pInputDevice, REL_WHEEL, dz);
1321 input_report_rel(g_pInputDevice, REL_HWHEEL, dw);
1322
1323 input_report_key(g_pInputDevice, BTN_LEFT, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_LEFT));
1324 input_report_key(g_pInputDevice, BTN_RIGHT, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_RIGHT));
1325 input_report_key(g_pInputDevice, BTN_MIDDLE, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_MIDDLE));
1326 input_report_key(g_pInputDevice, BTN_SIDE, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_X1));
1327 input_report_key(g_pInputDevice, BTN_EXTRA, RT_BOOL(fButtons & VMMDEV_MOUSE_BUTTON_X2));
1328 }
1329
1330# ifdef EV_SYN
1331 input_sync(g_pInputDevice);
1332# endif
1333 }
1334#endif
1335 Log3(("VGDrvNativeISRMousePollEvent: done\n"));
1336}
1337
1338
1339bool VGDrvNativeProcessOption(PVBOXGUESTDEVEXT pDevExt, const char *pszName, const char *pszValue)
1340{
1341 RT_NOREF(pDevExt); RT_NOREF(pszName); RT_NOREF(pszValue);
1342 return false;
1343}
1344
1345
1346#if RTLNX_VER_MIN(2,6,0)
1347
1348/** log and dbg_log parameter setter. */
1349static int vgdrvLinuxParamLogGrpSet(const char *pszValue, CONST_4_15 struct kernel_param *pParam)
1350{
1351 if (g_fLoggerCreated)
1352 {
1353 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1354 if (pLogger)
1355 RTLogGroupSettings(pLogger, pszValue);
1356 }
1357 else if (pParam->name[0] != 'd')
1358 strlcpy(&g_szLogGrp[0], pszValue, sizeof(g_szLogGrp));
1359
1360 return 0;
1361}
1362
1363/** log and dbg_log parameter getter. */
1364static int vgdrvLinuxParamLogGrpGet(char *pszBuf, CONST_4_15 struct kernel_param *pParam)
1365{
1366 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1367 *pszBuf = '\0';
1368 if (pLogger)
1369 RTLogQueryGroupSettings(pLogger, pszBuf, _4K);
1370 return strlen(pszBuf);
1371}
1372
1373
1374/** log and dbg_log_flags parameter setter. */
1375static int vgdrvLinuxParamLogFlagsSet(const char *pszValue, CONST_4_15 struct kernel_param *pParam)
1376{
1377 if (g_fLoggerCreated)
1378 {
1379 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1380 if (pLogger)
1381 RTLogFlags(pLogger, pszValue);
1382 }
1383 else if (pParam->name[0] != 'd')
1384 strlcpy(&g_szLogFlags[0], pszValue, sizeof(g_szLogFlags));
1385 return 0;
1386}
1387
1388/** log and dbg_log_flags parameter getter. */
1389static int vgdrvLinuxParamLogFlagsGet(char *pszBuf, CONST_4_15 struct kernel_param *pParam)
1390{
1391 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1392 *pszBuf = '\0';
1393 if (pLogger)
1394 RTLogQueryFlags(pLogger, pszBuf, _4K);
1395 return strlen(pszBuf);
1396}
1397
1398
1399/** log and dbg_log_dest parameter setter. */
1400static int vgdrvLinuxParamLogDstSet(const char *pszValue, CONST_4_15 struct kernel_param *pParam)
1401{
1402 if (g_fLoggerCreated)
1403 {
1404 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1405 if (pLogger)
1406 RTLogDestinations(pLogger, pszValue);
1407 }
1408 else if (pParam->name[0] != 'd')
1409 strlcpy(&g_szLogDst[0], pszValue, sizeof(g_szLogDst));
1410 return 0;
1411}
1412
1413/** log and dbg_log_dest parameter getter. */
1414static int vgdrvLinuxParamLogDstGet(char *pszBuf, CONST_4_15 struct kernel_param *pParam)
1415{
1416 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1417 *pszBuf = '\0';
1418 if (pLogger)
1419 RTLogQueryDestinations(pLogger, pszBuf, _4K);
1420 return strlen(pszBuf);
1421}
1422
1423
1424/** r3_log_to_host parameter setter. */
1425static int vgdrvLinuxParamR3LogToHostSet(const char *pszValue, CONST_4_15 struct kernel_param *pParam)
1426{
1427 g_DevExt.fLoggingEnabled = VBDrvCommonIsOptionValueTrue(pszValue);
1428 return 0;
1429}
1430
1431/** r3_log_to_host parameter getter. */
1432static int vgdrvLinuxParamR3LogToHostGet(char *pszBuf, CONST_4_15 struct kernel_param *pParam)
1433{
1434 strcpy(pszBuf, g_DevExt.fLoggingEnabled ? "enabled" : "disabled");
1435 return strlen(pszBuf);
1436}
1437
1438
1439/*
1440 * Define module parameters.
1441 */
1442module_param_call(log, vgdrvLinuxParamLogGrpSet, vgdrvLinuxParamLogGrpGet, NULL, 0664);
1443module_param_call(log_flags, vgdrvLinuxParamLogFlagsSet, vgdrvLinuxParamLogFlagsGet, NULL, 0664);
1444module_param_call(log_dest, vgdrvLinuxParamLogDstSet, vgdrvLinuxParamLogDstGet, NULL, 0664);
1445# ifdef LOG_ENABLED
1446module_param_call(dbg_log, vgdrvLinuxParamLogGrpSet, vgdrvLinuxParamLogGrpGet, NULL, 0664);
1447module_param_call(dbg_log_flags, vgdrvLinuxParamLogFlagsSet, vgdrvLinuxParamLogFlagsGet, NULL, 0664);
1448module_param_call(dbg_log_dest, vgdrvLinuxParamLogDstSet, vgdrvLinuxParamLogDstGet, NULL, 0664);
1449# endif
1450module_param_call(r3_log_to_host, vgdrvLinuxParamR3LogToHostSet, vgdrvLinuxParamR3LogToHostGet, NULL, 0664);
1451
1452#endif /* 2.6.0 and later */
1453
1454
1455module_init(vgdrvLinuxModInit);
1456module_exit(vgdrvLinuxModExit);
1457
1458MODULE_AUTHOR(VBOX_VENDOR);
1459MODULE_DESCRIPTION(VBOX_PRODUCT " Guest Additions for Linux Module");
1460MODULE_LICENSE("GPL");
1461#ifdef MODULE_VERSION
1462MODULE_VERSION(VBOX_VERSION_STRING " r" RT_XSTR(VBOX_SVN_REV));
1463#endif
1464
Note: See TracBrowser for help on using the repository browser.

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