VirtualBox

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

Last change on this file since 54645 was 54610, checked in by vboxsync, 10 years ago

fix

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