VirtualBox

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

Last change on this file was 106193, checked in by vboxsync, 6 weeks ago

Additions: Linux: vboxguest: Introduce initial support for kernel 6.12, bugref:10782.

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