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