VirtualBox

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

Last change on this file since 45221 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

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