VirtualBox

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

Last change on this file since 58053 was 58053, checked in by vboxsync, 9 years ago

VBoxGuest: s/vbgd/VGDrv/ + started on page.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.0 KB
Line 
1/* $Rev: 58053 $ */
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-2015 Oracle Corporation
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License (GPL) as published by the Free Software
16 * Foundation, in version 2 as it comes in the "COPYING" file of the
17 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 */
20
21
22/*********************************************************************************************************************************
23* Header Files *
24*********************************************************************************************************************************/
25#define LOG_GROUP LOG_GROUP_SUP_DRV
26
27#include "the-linux-kernel.h"
28
29#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 15)
30# define VBOXGUEST_WITH_INPUT_DRIVER
31#endif
32
33#include "VBoxGuestInternal.h"
34#ifdef VBOXGUEST_WITH_INPUT_DRIVER
35# include <linux/input.h>
36#endif
37#include <linux/miscdevice.h>
38#include <linux/poll.h>
39#include <VBox/version.h>
40
41#include <iprt/assert.h>
42#include <iprt/asm.h>
43#include <iprt/err.h>
44#include <iprt/initterm.h>
45#include <iprt/mem.h>
46#include <iprt/mp.h>
47#include <iprt/process.h>
48#include <iprt/spinlock.h>
49#include <iprt/semaphore.h>
50#include <VBox/log.h>
51
52
53/*********************************************************************************************************************************
54* Defined Constants And Macros *
55*********************************************************************************************************************************/
56/** The device name. */
57#define DEVICE_NAME "vboxguest"
58/** The device name for the device node open to everyone.. */
59#define DEVICE_NAME_USER "vboxuser"
60/** The name of the PCI driver */
61#define DRIVER_NAME DEVICE_NAME
62
63
64/* 2.4.x compatibility macros that may or may not be defined. */
65#ifndef IRQ_RETVAL
66# define irqreturn_t void
67# define IRQ_RETVAL(n)
68#endif
69
70
71/*********************************************************************************************************************************
72* Internal Functions *
73*********************************************************************************************************************************/
74static void vboxguestLinuxTermPci(struct pci_dev *pPciDev);
75static int vboxguestLinuxModInit(void);
76static void vboxguestLinuxModExit(void);
77static int vboxguestLinuxOpen(struct inode *pInode, struct file *pFilp);
78static int vboxguestLinuxRelease(struct inode *pInode, struct file *pFilp);
79#ifdef HAVE_UNLOCKED_IOCTL
80static long vboxguestLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
81#else
82static int vboxguestLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg);
83#endif
84static int vboxguestFAsync(int fd, struct file *pFile, int fOn);
85static unsigned int vboxguestPoll(struct file *pFile, poll_table *pPt);
86static ssize_t vboxguestRead(struct file *pFile, char *pbBuf, size_t cbRead, loff_t *poff);
87
88
89/*********************************************************************************************************************************
90* Global Variables *
91*********************************************************************************************************************************/
92/**
93 * Device extention & session data association structure.
94 */
95static VBOXGUESTDEVEXT g_DevExt;
96/** The PCI device. */
97static struct pci_dev *g_pPciDev = NULL;
98/** The base of the I/O port range. */
99static RTIOPORT g_IOPortBase;
100/** The base of the MMIO range. */
101static RTHCPHYS g_MMIOPhysAddr = NIL_RTHCPHYS;
102/** The size of the MMIO range as seen by PCI. */
103static uint32_t g_cbMMIO;
104/** The pointer to the mapping of the MMIO range. */
105static void *g_pvMMIOBase;
106/** Wait queue used by polling. */
107static wait_queue_head_t g_PollEventQueue;
108/** Asynchronous notification stuff. */
109static struct fasync_struct *g_pFAsyncQueue;
110#ifdef VBOXGUEST_WITH_INPUT_DRIVER
111/** Pre-allocated mouse status VMMDev request for use in the IRQ
112 * handler. */
113static VMMDevReqMouseStatus *g_pMouseStatusReq;
114#endif
115#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
116/** Whether we've create the logger or not. */
117static volatile bool g_fLoggerCreated;
118/** Release logger group settings. */
119static char g_szLogGrp[128];
120/** Release logger flags settings. */
121static char g_szLogFlags[128];
122/** Release logger destination settings. */
123static char g_szLogDst[128];
124# if 0
125/** Debug logger group settings. */
126static char g_szDbgLogGrp[128];
127/** Debug logger flags settings. */
128static char g_szDbgLogFlags[128];
129/** Debug logger destination settings. */
130static char g_szDbgLogDst[128];
131# endif
132#endif
133
134/** The input device handle */
135#ifdef VBOXGUEST_WITH_INPUT_DRIVER
136static struct input_dev *g_pInputDevice = NULL;
137#endif
138
139/** The file_operations structure. */
140static struct file_operations g_FileOps =
141{
142 owner: THIS_MODULE,
143 open: vboxguestLinuxOpen,
144 release: vboxguestLinuxRelease,
145#ifdef HAVE_UNLOCKED_IOCTL
146 unlocked_ioctl: vboxguestLinuxIOCtl,
147#else
148 ioctl: vboxguestLinuxIOCtl,
149#endif
150 fasync: vboxguestFAsync,
151 read: vboxguestRead,
152 poll: vboxguestPoll,
153 llseek: no_llseek,
154};
155
156/** The miscdevice structure. */
157static struct miscdevice g_MiscDevice =
158{
159 minor: MISC_DYNAMIC_MINOR,
160 name: DEVICE_NAME,
161 fops: &g_FileOps,
162};
163
164/** The file_operations structure for the user device.
165 * @remarks For the time being we'll be using the same implementation as
166 * /dev/vboxguest here. */
167static struct file_operations g_FileOpsUser =
168{
169 owner: THIS_MODULE,
170 open: vboxguestLinuxOpen,
171 release: vboxguestLinuxRelease,
172#ifdef HAVE_UNLOCKED_IOCTL
173 unlocked_ioctl: vboxguestLinuxIOCtl,
174#else
175 ioctl: vboxguestLinuxIOCtl,
176#endif
177};
178
179/** The miscdevice structure for the user device. */
180static struct miscdevice g_MiscDeviceUser =
181{
182 minor: MISC_DYNAMIC_MINOR,
183 name: DEVICE_NAME_USER,
184 fops: &g_FileOpsUser,
185};
186
187
188/** PCI hotplug structure. */
189static const struct pci_device_id
190#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
191__devinitdata
192#endif
193g_VBoxGuestPciId[] =
194{
195 {
196 vendor: VMMDEV_VENDORID,
197 device: VMMDEV_DEVICEID
198 },
199 {
200 /* empty entry */
201 }
202};
203MODULE_DEVICE_TABLE(pci, g_VBoxGuestPciId);
204
205static PVBOXGUESTSESSION g_pKernelSession = NULL;
206
207/**
208 * Converts a VBox status code to a linux error code.
209 *
210 * @returns corresponding negative linux error code.
211 * @param rc supdrv error code (SUPDRV_ERR_* defines).
212 */
213static int vboxguestLinuxConvertToNegErrno(int rc)
214{
215 if ( rc > -1000
216 && rc < 1000)
217 return -RTErrConvertToErrno(rc);
218 switch (rc)
219 {
220 case VERR_HGCM_SERVICE_NOT_FOUND: return -ESRCH;
221 case VINF_HGCM_CLIENT_REJECTED: return 0;
222 case VERR_HGCM_INVALID_CMD_ADDRESS: return -EFAULT;
223 case VINF_HGCM_ASYNC_EXECUTE: return 0;
224 case VERR_HGCM_INTERNAL: return -EPROTO;
225 case VERR_HGCM_INVALID_CLIENT_ID: return -EINVAL;
226 case VINF_HGCM_SAVE_STATE: return 0;
227 /* No reason to return this to a guest */
228 // case VERR_HGCM_SERVICE_EXISTS: return -EEXIST;
229 default:
230 AssertMsgFailed(("Unhandled error code %Rrc\n", rc));
231 return -EPROTO;
232 }
233}
234
235
236
237/**
238 * Does the PCI detection and init of the device.
239 *
240 * @returns 0 on success, negated errno on failure.
241 */
242static int vboxguestLinuxProbePci(struct pci_dev *pPciDev,
243 const struct pci_device_id *id)
244{
245 int rc;
246
247 NOREF(id);
248 AssertReturn(!g_pPciDev, -EINVAL);
249 rc = pci_enable_device(pPciDev);
250 if (rc >= 0)
251 {
252 /* I/O Ports are mandatory, the MMIO bit is not. */
253 g_IOPortBase = pci_resource_start(pPciDev, 0);
254 if (g_IOPortBase != 0)
255 {
256 /*
257 * Map the register address space.
258 */
259 g_MMIOPhysAddr = pci_resource_start(pPciDev, 1);
260 g_cbMMIO = pci_resource_len(pPciDev, 1);
261 if (request_mem_region(g_MMIOPhysAddr, g_cbMMIO, DEVICE_NAME) != NULL)
262 {
263 g_pvMMIOBase = ioremap(g_MMIOPhysAddr, g_cbMMIO);
264 if (g_pvMMIOBase)
265 {
266 /** @todo why aren't we requesting ownership of the I/O ports as well? */
267 g_pPciDev = pPciDev;
268 return 0;
269 }
270
271 /* failure cleanup path */
272 LogRel((DEVICE_NAME ": ioremap failed; MMIO Addr=%RHp cb=%#x\n", g_MMIOPhysAddr, g_cbMMIO));
273 rc = -ENOMEM;
274 release_mem_region(g_MMIOPhysAddr, g_cbMMIO);
275 }
276 else
277 {
278 LogRel((DEVICE_NAME ": failed to obtain adapter memory\n"));
279 rc = -EBUSY;
280 }
281 g_MMIOPhysAddr = NIL_RTHCPHYS;
282 g_cbMMIO = 0;
283 g_IOPortBase = 0;
284 }
285 else
286 {
287 LogRel((DEVICE_NAME ": did not find expected hardware resources\n"));
288 rc = -ENXIO;
289 }
290 pci_disable_device(pPciDev);
291 }
292 else
293 LogRel((DEVICE_NAME ": could not enable device: %d\n", rc));
294 return rc;
295}
296
297
298/**
299 * Clean up the usage of the PCI device.
300 */
301static void vboxguestLinuxTermPci(struct pci_dev *pPciDev)
302{
303 g_pPciDev = NULL;
304 if (pPciDev)
305 {
306 iounmap(g_pvMMIOBase);
307 g_pvMMIOBase = NULL;
308
309 release_mem_region(g_MMIOPhysAddr, g_cbMMIO);
310 g_MMIOPhysAddr = NIL_RTHCPHYS;
311 g_cbMMIO = 0;
312
313 pci_disable_device(pPciDev);
314 }
315}
316
317
318/** Structure for registering the PCI driver. */
319static struct pci_driver g_PciDriver =
320{
321 name: DRIVER_NAME,
322 id_table: g_VBoxGuestPciId,
323 probe: vboxguestLinuxProbePci,
324 remove: vboxguestLinuxTermPci
325};
326
327
328/**
329 * Interrupt service routine.
330 *
331 * @returns In 2.4 it returns void.
332 * In 2.6 we indicate whether we've handled the IRQ or not.
333 *
334 * @param iIrq The IRQ number.
335 * @param pvDevId The device ID, a pointer to g_DevExt.
336 * @param pvRegs Register set. Removed in 2.6.19.
337 */
338#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
339static irqreturn_t vboxguestLinuxISR(int iIrrq, void *pvDevId)
340#else
341static irqreturn_t vboxguestLinuxISR(int iIrrq, void *pvDevId, struct pt_regs *pRegs)
342#endif
343{
344 bool fTaken = VGDrvCommonISR(&g_DevExt);
345 return IRQ_RETVAL(fTaken);
346}
347
348
349/**
350 * Registers the ISR and initializes the poll wait queue.
351 */
352static int __init vboxguestLinuxInitISR(void)
353{
354 int rc;
355
356 init_waitqueue_head(&g_PollEventQueue);
357 rc = request_irq(g_pPciDev->irq,
358 vboxguestLinuxISR,
359#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
360 IRQF_SHARED,
361#else
362 SA_SHIRQ,
363#endif
364 DEVICE_NAME,
365 &g_DevExt);
366 if (rc)
367 {
368 LogRel((DEVICE_NAME ": could not request IRQ %d: err=%d\n", g_pPciDev->irq, rc));
369 return rc;
370 }
371 return 0;
372}
373
374
375/**
376 * Deregisters the ISR.
377 */
378static void vboxguestLinuxTermISR(void)
379{
380 free_irq(g_pPciDev->irq, &g_DevExt);
381}
382
383
384#ifdef VBOXGUEST_WITH_INPUT_DRIVER
385/** Calls the kernel IOCtl to report mouse status to the host on behalf of
386 * our kernel session. */
387static int vboxguestLinuxSetMouseStatus(uint32_t fStatus)
388{
389 return VGDrvCommonIoCtl(VBOXGUEST_IOCTL_SET_MOUSE_STATUS, &g_DevExt, g_pKernelSession, &fStatus, sizeof(fStatus), NULL);
390}
391
392
393/** Called when the input device is first opened. Sets up absolute reporting.
394 */
395static int vboxguestOpenInputDevice(struct input_dev *pDev)
396{
397 int rc= vboxguestLinuxSetMouseStatus(VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE | VMMDEV_MOUSE_NEW_PROTOCOL);
398 if (RT_FAILURE(rc))
399 return ENODEV;
400 NOREF(pDev);
401 return 0;
402}
403
404
405/** Called if all open handles to the device are closed, disables absolute
406 * reporting. */
407static void vboxguestCloseInputDevice(struct input_dev *pDev)
408{
409 NOREF(pDev);
410 vboxguestLinuxSetMouseStatus(0);
411}
412
413
414/**
415 * Creates the kernel input device.
416 */
417static int __init vboxguestLinuxCreateInputDevice(void)
418{
419 int rc;
420
421 rc = VbglGRAlloc((VMMDevRequestHeader **)&g_pMouseStatusReq,
422 sizeof(*g_pMouseStatusReq),
423 VMMDevReq_GetMouseStatus);
424 if (RT_FAILURE(rc))
425 return -ENOMEM;
426 g_pInputDevice = input_allocate_device();
427 if (!g_pInputDevice)
428 {
429 VbglGRFree(&g_pMouseStatusReq->header);
430 return -ENOMEM;
431 }
432 g_pInputDevice->id.bustype = BUS_PCI;
433 g_pInputDevice->id.vendor = VMMDEV_VENDORID;
434 g_pInputDevice->id.product = VMMDEV_DEVICEID;
435 g_pInputDevice->id.version = VBOX_SHORT_VERSION;
436 g_pInputDevice->open = vboxguestOpenInputDevice;
437 g_pInputDevice->close = vboxguestCloseInputDevice;
438# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
439 g_pInputDevice->cdev.dev = &g_pPciDev->dev;
440# else
441 g_pInputDevice->dev.parent = &g_pPciDev->dev;
442# endif
443 {
444 int rc = input_register_device(g_pInputDevice);
445 if (rc)
446 {
447 VbglGRFree(&g_pMouseStatusReq->header);
448 input_free_device(g_pInputDevice);
449 return rc;
450 }
451 }
452 /* Do what one of our competitors apparently does as that works. */
453 ASMBitSet(g_pInputDevice->evbit, EV_ABS);
454 ASMBitSet(g_pInputDevice->evbit, EV_KEY);
455# ifdef EV_SYN
456 ASMBitSet(g_pInputDevice->evbit, EV_SYN);
457# endif
458 input_set_abs_params(g_pInputDevice, ABS_X, VMMDEV_MOUSE_RANGE_MIN,
459 VMMDEV_MOUSE_RANGE_MAX, 0, 0);
460 input_set_abs_params(g_pInputDevice, ABS_Y, VMMDEV_MOUSE_RANGE_MIN,
461 VMMDEV_MOUSE_RANGE_MAX, 0, 0);
462 ASMBitSet(g_pInputDevice->keybit, BTN_MOUSE);
463 /** @todo this string should be in a header file somewhere. */
464 g_pInputDevice->name = "VirtualBox mouse integration";
465 return 0;
466}
467
468
469/**
470 * Terminates the kernel input device.
471 */
472static void vboxguestLinuxTermInputDevice(void)
473{
474 VbglGRFree(&g_pMouseStatusReq->header);
475 /* See documentation of input_register_device(): input_free_device()
476 * should not be called after a device has been registered. */
477 input_unregister_device(g_pInputDevice);
478}
479#endif /* VBOXGUEST_WITH_INPUT_DRIVER */
480
481
482/**
483 * Creates the device nodes.
484 *
485 * @returns 0 on success, negated errno on failure.
486 */
487static int __init vboxguestLinuxInitDeviceNodes(void)
488{
489 int rc;
490
491 /*
492 * The full feature device node.
493 */
494 rc = misc_register(&g_MiscDevice);
495 if (rc)
496 {
497 LogRel((DEVICE_NAME ": misc_register failed for %s (rc=%d)\n", DEVICE_NAME, rc));
498 return rc;
499 }
500
501 /*
502 * The device node intended to be accessible by all users.
503 */
504 rc = misc_register(&g_MiscDeviceUser);
505 if (rc)
506 {
507 LogRel((DEVICE_NAME ": misc_register failed for %s (rc=%d)\n", DEVICE_NAME_USER, rc));
508 misc_deregister(&g_MiscDevice);
509 return rc;
510 }
511
512 return 0;
513}
514
515
516/**
517 * Deregisters the device nodes.
518 */
519static void vboxguestLinuxTermDeviceNodes(void)
520{
521 misc_deregister(&g_MiscDevice);
522 misc_deregister(&g_MiscDeviceUser);
523}
524
525
526/**
527 * Initialize module.
528 *
529 * @returns appropriate status code.
530 */
531static int __init vboxguestLinuxModInit(void)
532{
533 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
534 PRTLOGGER pRelLogger;
535 int rc;
536
537 /*
538 * Initialize IPRT first.
539 */
540 rc = RTR0Init(0);
541 if (RT_FAILURE(rc))
542 {
543 printk(KERN_ERR DEVICE_NAME ": RTR0Init failed, rc=%d.\n", rc);
544 return -EINVAL;
545 }
546
547 /*
548 * Create the release log.
549 * (We do that here instead of common code because we want to log
550 * early failures using the LogRel macro.)
551 */
552 rc = RTLogCreate(&pRelLogger, 0 /* fFlags */, "all",
553 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
554 RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER | RTLOGDEST_USER, NULL);
555 if (RT_SUCCESS(rc))
556 {
557#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
558 RTLogGroupSettings(pRelLogger, g_szLogGrp);
559 RTLogFlags(pRelLogger, g_szLogFlags);
560 RTLogDestinations(pRelLogger, g_szLogDst);
561#endif
562 RTLogRelSetDefaultInstance(pRelLogger);
563 }
564#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
565 g_fLoggerCreated = true;
566#endif
567
568 /*
569 * Locate and initialize the PCI device.
570 */
571 rc = pci_register_driver(&g_PciDriver);
572 if (rc >= 0 && g_pPciDev)
573 {
574 /*
575 * Register the interrupt service routine for it.
576 */
577 rc = vboxguestLinuxInitISR();
578 if (rc >= 0)
579 {
580 /*
581 * Call the common device extension initializer.
582 */
583#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) && defined(RT_ARCH_X86)
584 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux26;
585#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) && defined(RT_ARCH_AMD64)
586 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux26_x64;
587#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) && defined(RT_ARCH_X86)
588 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux24;
589#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0) && defined(RT_ARCH_AMD64)
590 VBOXOSTYPE enmOSType = VBOXOSTYPE_Linux24_x64;
591#else
592# warning "huh? which arch + version is this?"
593 VBOXOSTYPE enmOsType = VBOXOSTYPE_Linux;
594#endif
595 rc = VGDrvCommonInitDevExt(&g_DevExt,
596 g_IOPortBase,
597 g_pvMMIOBase,
598 g_cbMMIO,
599 enmOSType,
600 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
601 if (RT_SUCCESS(rc))
602 {
603 /*
604 * Create the kernel session for this driver.
605 */
606 rc = VGDrvCommonCreateKernelSession(&g_DevExt, &g_pKernelSession);
607 if (RT_SUCCESS(rc))
608 {
609 /*
610 * Create the kernel input device.
611 */
612#ifdef VBOXGUEST_WITH_INPUT_DRIVER
613 rc = vboxguestLinuxCreateInputDevice();
614 if (rc >= 0)
615 {
616#endif
617 /*
618 * Finally, create the device nodes.
619 */
620 rc = vboxguestLinuxInitDeviceNodes();
621 if (rc >= 0)
622 {
623 /* some useful information for the user but don't show this on the console */
624 LogRel((DEVICE_NAME ": misc device minor %d, IRQ %d, I/O port %RTiop, MMIO at %RHp (size 0x%x)\n",
625 g_MiscDevice.minor, g_pPciDev->irq, g_IOPortBase, g_MMIOPhysAddr, g_cbMMIO));
626 printk(KERN_DEBUG DEVICE_NAME ": Successfully loaded version "
627 VBOX_VERSION_STRING " (interface " RT_XSTR(VMMDEV_VERSION) ")\n");
628 return rc;
629 }
630
631 /* bail out */
632#ifdef VBOXGUEST_WITH_INPUT_DRIVER
633 vboxguestLinuxTermInputDevice();
634 }
635 else
636 {
637 LogRel((DEVICE_NAME ": vboxguestCreateInputDevice failed with rc=%Rrc\n", rc));
638 rc = RTErrConvertFromErrno(rc);
639 }
640#endif
641 VGDrvCommonCloseSession(&g_DevExt, g_pKernelSession);
642 }
643 VGDrvCommonDeleteDevExt(&g_DevExt);
644 }
645 else
646 {
647 LogRel((DEVICE_NAME ": VbgdCommonInitDevExt failed with rc=%Rrc\n", rc));
648 rc = RTErrConvertFromErrno(rc);
649 }
650 vboxguestLinuxTermISR();
651 }
652 }
653 else
654 {
655 LogRel((DEVICE_NAME ": PCI device not found, probably running on physical hardware.\n"));
656 rc = -ENODEV;
657 }
658 pci_unregister_driver(&g_PciDriver);
659 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
660 RTLogDestroy(RTLogSetDefaultInstance(NULL));
661 RTR0Term();
662 return rc;
663}
664
665
666/**
667 * Unload the module.
668 */
669static void __exit vboxguestLinuxModExit(void)
670{
671 /*
672 * Inverse order of init.
673 */
674 vboxguestLinuxTermDeviceNodes();
675#ifdef VBOXGUEST_WITH_INPUT_DRIVER
676 vboxguestLinuxTermInputDevice();
677#endif
678 VGDrvCommonCloseSession(&g_DevExt, g_pKernelSession);
679 VGDrvCommonDeleteDevExt(&g_DevExt);
680 vboxguestLinuxTermISR();
681 pci_unregister_driver(&g_PciDriver);
682 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
683 RTLogDestroy(RTLogSetDefaultInstance(NULL));
684 RTR0Term();
685}
686
687
688/**
689 * Device open. Called on open /dev/vboxdrv
690 *
691 * @param pInode Pointer to inode info structure.
692 * @param pFilp Associated file pointer.
693 */
694static int vboxguestLinuxOpen(struct inode *pInode, struct file *pFilp)
695{
696 int rc;
697 PVBOXGUESTSESSION pSession;
698 Log((DEVICE_NAME ": pFilp=%p pid=%d/%d %s\n", pFilp, RTProcSelf(), current->pid, current->comm));
699
700 /*
701 * Call common code to create the user session. Associate it with
702 * the file so we can access it in the other methods.
703 */
704 rc = VGDrvCommonCreateUserSession(&g_DevExt, &pSession);
705 if (RT_SUCCESS(rc))
706 {
707 pFilp->private_data = pSession;
708 if (MINOR(pInode->i_rdev) == g_MiscDeviceUser.minor)
709 pSession->fUserSession = true;
710 }
711
712 Log(("vboxguestLinuxOpen: g_DevExt=%p pSession=%p rc=%d/%d (pid=%d/%d %s)\n",
713 &g_DevExt, pSession, rc, vboxguestLinuxConvertToNegErrno(rc),
714 RTProcSelf(), current->pid, current->comm));
715 return vboxguestLinuxConvertToNegErrno(rc);
716}
717
718
719/**
720 * Close device.
721 *
722 * @param pInode Pointer to inode info structure.
723 * @param pFilp Associated file pointer.
724 */
725static int vboxguestLinuxRelease(struct inode *pInode, struct file *pFilp)
726{
727 Log(("vboxguestLinuxRelease: pFilp=%p pSession=%p pid=%d/%d %s\n",
728 pFilp, pFilp->private_data, RTProcSelf(), current->pid, current->comm));
729
730#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
731 /* This housekeeping was needed in older kernel versions to ensure that
732 * the file pointer didn't get left on the polling queue. */
733 vboxguestFAsync(-1, pFilp, 0);
734#endif
735 VGDrvCommonCloseSession(&g_DevExt, (PVBOXGUESTSESSION)pFilp->private_data);
736 pFilp->private_data = NULL;
737 return 0;
738}
739
740
741/**
742 * Device I/O Control entry point.
743 *
744 * @param pFilp Associated file pointer.
745 * @param uCmd The function specified to ioctl().
746 * @param ulArg The argument specified to ioctl().
747 */
748#ifdef HAVE_UNLOCKED_IOCTL
749static long vboxguestLinuxIOCtl(struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
750#else
751static int vboxguestLinuxIOCtl(struct inode *pInode, struct file *pFilp, unsigned int uCmd, unsigned long ulArg)
752#endif
753{
754 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFilp->private_data;
755 uint32_t cbData = _IOC_SIZE(uCmd);
756 void *pvBufFree;
757 void *pvBuf;
758 int rc;
759 uint64_t au64Buf[32/sizeof(uint64_t)];
760
761 Log6(("vboxguestLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p pid=%d/%d\n", pFilp, uCmd, (void *)ulArg, RTProcSelf(), current->pid));
762
763 /*
764 * Buffer the request.
765 */
766 if (cbData <= sizeof(au64Buf))
767 {
768 pvBufFree = NULL;
769 pvBuf = &au64Buf[0];
770 }
771 else
772 {
773 pvBufFree = pvBuf = RTMemTmpAlloc(cbData);
774 if (RT_UNLIKELY(!pvBuf))
775 {
776 LogRel((DEVICE_NAME "::IOCtl: RTMemTmpAlloc failed to alloc %u bytes.\n", cbData));
777 return -ENOMEM;
778 }
779 }
780 if (RT_LIKELY(copy_from_user(pvBuf, (void *)ulArg, cbData) == 0))
781 {
782 /*
783 * Process the IOCtl.
784 */
785 size_t cbDataReturned;
786 rc = VGDrvCommonIoCtl(uCmd, &g_DevExt, pSession, pvBuf, cbData, &cbDataReturned);
787
788 /*
789 * Copy ioctl data and output buffer back to user space.
790 */
791 if (RT_SUCCESS(rc))
792 {
793 rc = 0;
794 if (RT_UNLIKELY(cbDataReturned > cbData))
795 {
796 LogRel((DEVICE_NAME "::IOCtl: too much output data %u expected %u\n", cbDataReturned, cbData));
797 cbDataReturned = cbData;
798 }
799 if (cbDataReturned > 0)
800 {
801 if (RT_UNLIKELY(copy_to_user((void *)ulArg, pvBuf, cbDataReturned) != 0))
802 {
803 LogRel((DEVICE_NAME "::IOCtl: copy_to_user failed; pvBuf=%p ulArg=%p cbDataReturned=%u uCmd=%d\n",
804 pvBuf, (void *)ulArg, cbDataReturned, uCmd, rc));
805 rc = -EFAULT;
806 }
807 }
808 }
809 else
810 {
811 Log(("vboxguestLinuxIOCtl: pFilp=%p uCmd=%#x ulArg=%p failed, rc=%d\n", pFilp, uCmd, (void *)ulArg, rc));
812 rc = -rc; Assert(rc > 0); /* Positive returns == negated VBox error status codes. */
813 }
814 }
815 else
816 {
817 Log((DEVICE_NAME "::IOCtl: copy_from_user(,%#lx, %#x) failed; uCmd=%#x.\n", ulArg, cbData, uCmd));
818 rc = -EFAULT;
819 }
820 if (pvBufFree)
821 RTMemFree(pvBufFree);
822
823 Log6(("vboxguestLinuxIOCtl: returns %d (pid=%d/%d)\n", rc, RTProcSelf(), current->pid));
824 return rc;
825}
826
827
828/**
829 * Asynchronous notification activation method.
830 *
831 * @returns 0 on success, negative errno on failure.
832 *
833 * @param fd The file descriptor.
834 * @param pFile The file structure.
835 * @param fOn On/off indicator.
836 */
837static int vboxguestFAsync(int fd, struct file *pFile, int fOn)
838{
839 return fasync_helper(fd, pFile, fOn, &g_pFAsyncQueue);
840}
841
842
843/**
844 * Poll function.
845 *
846 * This returns ready to read if the mouse pointer mode or the pointer position
847 * has changed since last call to read.
848 *
849 * @returns 0 if no changes, POLLIN | POLLRDNORM if there are unseen changes.
850 *
851 * @param pFile The file structure.
852 * @param pPt The poll table.
853 *
854 * @remarks This is probably not really used, X11 is said to use the fasync
855 * interface instead.
856 */
857static unsigned int vboxguestPoll(struct file *pFile, poll_table *pPt)
858{
859 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFile->private_data;
860 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
861 unsigned int fMask = pSession->u32MousePosChangedSeq != u32CurSeq
862 ? POLLIN | POLLRDNORM
863 : 0;
864 poll_wait(pFile, &g_PollEventQueue, pPt);
865 return fMask;
866}
867
868
869/**
870 * Read to go with our poll/fasync response.
871 *
872 * @returns 1 or -EINVAL.
873 *
874 * @param pFile The file structure.
875 * @param pbBuf The buffer to read into.
876 * @param cbRead The max number of bytes to read.
877 * @param poff The current file position.
878 *
879 * @remarks This is probably not really used as X11 lets the driver do its own
880 * event reading. The poll condition is therefore also cleared when we
881 * see VMMDevReq_GetMouseStatus in VbgdCommonIoCtl_VMMRequest.
882 */
883static ssize_t vboxguestRead(struct file *pFile, char *pbBuf, size_t cbRead, loff_t *poff)
884{
885 PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFile->private_data;
886 uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq);
887
888 if (*poff != 0)
889 return -EINVAL;
890
891 /*
892 * Fake a single byte read if we're not up to date with the current mouse position.
893 */
894 if ( pSession->u32MousePosChangedSeq != u32CurSeq
895 && cbRead > 0)
896 {
897 pSession->u32MousePosChangedSeq = u32CurSeq;
898 pbBuf[0] = 0;
899 return 1;
900 }
901 return 0;
902}
903
904
905void VGDrvNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
906{
907#ifdef VBOXGUEST_WITH_INPUT_DRIVER
908 int rc;
909#endif
910 NOREF(pDevExt);
911
912 /*
913 * Wake up everyone that's in a poll() and post anyone that has
914 * subscribed to async notifications.
915 */
916 Log3(("VGDrvNativeISRMousePollEvent: wake_up_all\n"));
917 wake_up_all(&g_PollEventQueue);
918 Log3(("VGDrvNativeISRMousePollEvent: kill_fasync\n"));
919 kill_fasync(&g_pFAsyncQueue, SIGIO, POLL_IN);
920#ifdef VBOXGUEST_WITH_INPUT_DRIVER
921 /* Report events to the kernel input device */
922 g_pMouseStatusReq->mouseFeatures = 0;
923 g_pMouseStatusReq->pointerXPos = 0;
924 g_pMouseStatusReq->pointerYPos = 0;
925 rc = VbglGRPerform(&g_pMouseStatusReq->header);
926 if (RT_SUCCESS(rc))
927 {
928 input_report_abs(g_pInputDevice, ABS_X,
929 g_pMouseStatusReq->pointerXPos);
930 input_report_abs(g_pInputDevice, ABS_Y,
931 g_pMouseStatusReq->pointerYPos);
932# ifdef EV_SYN
933 input_sync(g_pInputDevice);
934# endif
935 }
936#endif
937 Log3(("VGDrvNativeISRMousePollEvent: done\n"));
938}
939
940
941/* Common code that depend on g_DevExt. */
942#include "VBoxGuestIDC-unix.c.h"
943
944EXPORT_SYMBOL(VBoxGuestIDCOpen);
945EXPORT_SYMBOL(VBoxGuestIDCClose);
946EXPORT_SYMBOL(VBoxGuestIDCCall);
947
948
949#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
950
951/** log and dbg_log parameter setter. */
952static int vboxguestLinuxParamLogGrpSet(const char *pszValue, struct kernel_param *pParam)
953{
954 if (g_fLoggerCreated)
955 {
956 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
957 if (pLogger)
958 RTLogGroupSettings(pLogger, pszValue);
959 }
960 else if (pParam->name[0] != 'd')
961 strlcpy(&g_szLogGrp[0], pszValue, sizeof(g_szLogGrp));
962
963 return 0;
964}
965
966/** log and dbg_log parameter getter. */
967static int vboxguestLinuxParamLogGrpGet(char *pszBuf, struct kernel_param *pParam)
968{
969 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
970 *pszBuf = '\0';
971 if (pLogger)
972 RTLogGetGroupSettings(pLogger, pszBuf, _4K);
973 return strlen(pszBuf);
974}
975
976
977/** log and dbg_log_flags parameter setter. */
978static int vboxguestLinuxParamLogFlagsSet(const char *pszValue, struct kernel_param *pParam)
979{
980 if (g_fLoggerCreated)
981 {
982 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
983 if (pLogger)
984 RTLogFlags(pLogger, pszValue);
985 }
986 else if (pParam->name[0] != 'd')
987 strlcpy(&g_szLogFlags[0], pszValue, sizeof(g_szLogFlags));
988 return 0;
989}
990
991/** log and dbg_log_flags parameter getter. */
992static int vboxguestLinuxParamLogFlagsGet(char *pszBuf, struct kernel_param *pParam)
993{
994 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
995 *pszBuf = '\0';
996 if (pLogger)
997 RTLogGetFlags(pLogger, pszBuf, _4K);
998 return strlen(pszBuf);
999}
1000
1001
1002/** log and dbg_log_dest parameter setter. */
1003static int vboxguestLinuxParamLogDstSet(const char *pszValue, struct kernel_param *pParam)
1004{
1005 if (g_fLoggerCreated)
1006 {
1007 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1008 if (pLogger)
1009 RTLogDestinations(pLogger, pszValue);
1010 }
1011 else if (pParam->name[0] != 'd')
1012 strlcpy(&g_szLogDst[0], pszValue, sizeof(g_szLogDst));
1013 return 0;
1014}
1015
1016/** log and dbg_log_dest parameter getter. */
1017static int vboxguestLinuxParamLogDstGet(char *pszBuf, struct kernel_param *pParam)
1018{
1019 PRTLOGGER pLogger = pParam->name[0] == 'd' ? RTLogDefaultInstance() : RTLogRelGetDefaultInstance();
1020 *pszBuf = '\0';
1021 if (pLogger)
1022 RTLogGetDestinations(pLogger, pszBuf, _4K);
1023 return strlen(pszBuf);
1024}
1025
1026
1027/** r3_log_to_host parameter setter. */
1028static int vboxguestLinuxParamR3LogToHostSet(const char *pszValue, struct kernel_param *pParam)
1029{
1030 if ( pszValue == NULL
1031 || *pszValue == '\0'
1032 || *pszValue == 'n'
1033 || *pszValue == 'N'
1034 || *pszValue == 'd'
1035 || *pszValue == 'D'
1036 || ( (*pszValue == 'o' || *pszValue == 'O')
1037 && (*pszValue == 'f' || *pszValue == 'F') )
1038 )
1039 g_DevExt.fLoggingEnabled = false;
1040 else
1041 g_DevExt.fLoggingEnabled = true;
1042 return 0;
1043}
1044
1045/** r3_log_to_host parameter getter. */
1046static int vboxguestLinuxParamR3LogToHostGet(char *pszBuf, struct kernel_param *pParam)
1047{
1048 strcpy(pszBuf, g_DevExt.fLoggingEnabled ? "enabled" : "disabled");
1049 return strlen(pszBuf);
1050}
1051
1052
1053/*
1054 * Define module parameters.
1055 */
1056module_param_call(log, vboxguestLinuxParamLogGrpSet, vboxguestLinuxParamLogGrpGet, NULL, 0664);
1057module_param_call(log_flags, vboxguestLinuxParamLogFlagsSet, vboxguestLinuxParamLogFlagsGet, NULL, 0664);
1058module_param_call(log_dest, vboxguestLinuxParamLogDstSet, vboxguestLinuxParamLogDstGet, NULL, 0664);
1059# ifdef LOG_ENABLED
1060module_param_call(dbg_log, vboxguestLinuxParamLogGrpSet, vboxguestLinuxParamLogGrpGet, NULL, 0664);
1061module_param_call(dbg_log_flags, vboxguestLinuxParamLogFlagsSet, vboxguestLinuxParamLogFlagsGet, NULL, 0664);
1062module_param_call(dbg_log_dest, vboxguestLinuxParamLogDstSet, vboxguestLinuxParamLogDstGet, NULL, 0664);
1063# endif
1064module_param_call(r3_log_to_host, vboxguestLinuxParamR3LogToHostSet, vboxguestLinuxParamR3LogToHostGet, NULL, 0664);
1065
1066#endif /* 2.6.0 and later */
1067
1068
1069module_init(vboxguestLinuxModInit);
1070module_exit(vboxguestLinuxModExit);
1071
1072MODULE_AUTHOR(VBOX_VENDOR);
1073MODULE_DESCRIPTION(VBOX_PRODUCT " Guest Additions for Linux Module");
1074MODULE_LICENSE("GPL");
1075#ifdef MODULE_VERSION
1076MODULE_VERSION(VBOX_VERSION_STRING);
1077#endif
1078
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