VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris-streams.c@ 41683

Last change on this file since 41683 was 41212, checked in by vboxsync, 13 years ago

Additions/solaris: updates to the streams driver.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 62.7 KB
Line 
1/* $Id: VBoxGuest-solaris-streams.c 41212 2012-05-08 15:55:06Z vboxsync $ */
2/** @file
3 * VirtualBox Guest Additions Driver for Solaris.
4 */
5
6/*
7 * Copyright (C) 2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/******************************************************************************
20* Header Files *
21******************************************************************************/
22
23#ifndef TESTCASE
24# include <sys/conf.h>
25# include <sys/modctl.h>
26# include <sys/msio.h>
27# include <sys/mutex.h>
28# include <sys/pci.h>
29# include <sys/stat.h>
30# include <sys/ddi.h>
31# include <sys/ddi_intr.h>
32# include <sys/open.h>
33# include <sys/strsun.h>
34# include <sys/stropts.h>
35# include <sys/sunddi.h>
36# include <sys/sunldi.h>
37# include <sys/vuid_event.h>
38# include <sys/vuid_wheel.h>
39# include <sys/file.h>
40#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */
41#else /* TESTCASE */
42# undef IN_RING3
43# define IN_RING0
44#endif /* TESTCASE */
45
46#include "VBoxGuestInternal.h"
47#include <VBox/log.h>
48#include <VBox/version.h>
49#include <iprt/assert.h>
50#include <iprt/initterm.h>
51#include <iprt/process.h>
52#include <iprt/mem.h>
53#include <iprt/cdefs.h>
54#include <iprt/asm.h>
55#include <iprt/string.h>
56
57#ifdef TESTCASE /* Include this last as we . */
58# include "testcase/solaris.h"
59# include <iprt/test.h>
60#endif /* TESTCASE */
61
62
63/******************************************************************************
64* Defined Constants And Macros *
65******************************************************************************/
66
67/** The module name. */
68#define DEVICE_NAME "vboxguest"
69/** The module description as seen in 'modinfo'. */
70#define DEVICE_DESC "VirtualBox GstDrv"
71/** The maximum number of open device nodes we support. */
72#define MAX_OPEN_NODES 4096
73
74
75/******************************************************************************
76* Internal functions used in global structures *
77******************************************************************************/
78
79static int vbgr0SolOpen(queue_t *pReadQueue, dev_t *pDev, int fFlag,
80 int fMode, cred_t *pCred);
81static int vbgr0SolClose(queue_t *pReadQueue, int fFlag, cred_t *pCred);
82static int vbgr0SolWPut(queue_t *pWriteQueue, mblk_t *pMBlk);
83
84static int vbgr0SolGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pArg, void **ppResult);
85static int vbgr0SolAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
86static int vbgr0SolDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
87
88
89/******************************************************************************
90* Driver global structures *
91******************************************************************************/
92
93#ifndef TESTCASE /* I see no value in including these in the test. */
94
95/*
96 * mod_info: STREAMS module information.
97 */
98static struct module_info g_vbgr0SolModInfo =
99{
100 0x0ffff, /* module id number */
101 "vboxguest",
102 0, /* minimum packet size */
103 INFPSZ, /* maximum packet size accepted */
104 512, /* high water mark for data flow control */
105 128 /* low water mark */
106};
107
108/*
109 * rinit: read queue structure for handling messages coming from below. In
110 * our case this means the host and the virtual hardware, so we do not need
111 * the put and service procedures.
112 */
113static struct qinit g_vbgr0SolRInit =
114{
115 NULL, /* put */
116 NULL, /* service thread procedure */
117 vbgr0SolOpen,
118 vbgr0SolClose,
119 NULL, /* reserved */
120 &g_vbgr0SolModInfo,
121 NULL /* module statistics structure */
122};
123
124/*
125 * winit: write queue structure for handling messages coming from above. Above
126 * means user space applications: either Guest Additions user space tools or
127 * applications reading pointer input. Messages from the last most likely pass
128 * through at least the "consms" console mouse streams module which multiplexes
129 * hardware pointer drivers to a single virtual pointer.
130 */
131static struct qinit g_vbgr0SolWInit =
132{
133 vbgr0SolWPut,
134 NULL, /* service thread procedure */
135 NULL, /* open */
136 NULL, /* close */
137 NULL, /* reserved */
138 &g_vbgr0SolModInfo,
139 NULL /* module statistics structure */
140};
141
142/**
143 * streamtab: for drivers that support char/block entry points.
144 */
145static struct streamtab g_vbgr0SolStreamTab =
146{
147 &g_vbgr0SolRInit,
148 &g_vbgr0SolWInit,
149 NULL, /* MUX rinit */
150 NULL /* MUX winit */
151};
152
153/**
154 * cb_ops: for drivers that support char/block entry points.
155 */
156static struct cb_ops g_vbgr0SolCbOps =
157{
158 nulldev, /* open */
159 nulldev, /* close */
160 nulldev, /* b strategy */
161 nulldev, /* b dump */
162 nulldev, /* b print */
163 nulldev, /* c read */
164 nulldev, /* c write */
165 nulldev, /* c ioctl */
166 nulldev, /* c devmap */
167 nulldev, /* c mmap */
168 nulldev, /* c segmap */
169 nochpoll, /* c poll */
170 ddi_prop_op, /* property ops */
171 &g_vbgr0SolStreamTab,
172 D_NEW | D_MP, /* compat. flag */
173};
174
175/**
176 * dev_ops: for driver device operations.
177 */
178static struct dev_ops g_vbgr0SolDevOps =
179{
180 DEVO_REV, /* driver build revision */
181 0, /* ref count */
182 vbgr0SolGetInfo,
183 nulldev, /* identify */
184 nulldev, /* probe */
185 vbgr0SolAttach,
186 vbgr0SolDetach,
187 nodev, /* reset */
188 &g_vbgr0SolCbOps,
189 (struct bus_ops *)0,
190 nodev /* power */
191};
192
193/**
194 * modldrv: export driver specifics to the kernel.
195 */
196static struct modldrv g_vbgr0SolModule =
197{
198 &mod_driverops, /* extern from kernel */
199 DEVICE_DESC " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
200 &g_vbgr0SolDevOps
201};
202
203/**
204 * modlinkage: export install/remove/info to the kernel.
205 */
206static struct modlinkage g_vbgr0SolModLinkage =
207{
208 MODREV_1, /* loadable module system revision */
209 &g_vbgr0SolModule,
210 NULL /* terminate array of linkage structures */
211};
212
213#else /* TESTCASE */
214static void *g_vbgr0SolModLinkage;
215#endif /* TESTCASE */
216
217/**
218 * State info for each open file handle.
219 */
220typedef struct
221{
222 /** Pointer to the session handle. */
223 PVBOXGUESTSESSION pSession;
224 /** The STREAMS write queue which we need for sending messages up to
225 * user-space. */
226 queue_t *pWriteQueue;
227 /* The current greatest horizontal pixel offset on the screen, used for
228 * absolute mouse position reporting.
229 */
230 int cMaxScreenX;
231 /* The current greatest vertical pixel offset on the screen, used for
232 * absolute mouse position reporting.
233 */
234 int cMaxScreenY;
235} VBGR0STATE, *PVBGR0STATE;
236
237
238/******************************************************************************
239* Global Variables *
240******************************************************************************/
241
242/** Device handle (we support only one instance). */
243static dev_info_t *g_pDip = NULL;
244/** Array of state structures for open device nodes. I don't care about
245 * wasting a few K of memory. */
246static VBGR0STATE g_aOpenNodeStates[MAX_OPEN_NODES] /* = { 0 } */;
247/** Mutex to protect the queue pointers in the node states from being unset
248 * during an IRQ. */
249static kmutex_t g_StateMutex;
250/** Device extention & session data association structure. */
251static VBOXGUESTDEVEXT g_DevExt;
252/** IO port handle. */
253static ddi_acc_handle_t g_PciIOHandle;
254/** MMIO handle. */
255static ddi_acc_handle_t g_PciMMIOHandle;
256/** IO Port. */
257static uint16_t g_uIOPortBase;
258/** Address of the MMIO region.*/
259static char *g_pMMIOBase; /* Actually caddr_t. */
260/** Size of the MMIO region. */
261static off_t g_cbMMIO;
262/** Pointer to the interrupt handle vector */
263static ddi_intr_handle_t *g_pIntr;
264/** Number of actually allocated interrupt handles */
265static size_t g_cIntrAllocated;
266/** The IRQ Mutex */
267static kmutex_t g_IrqMutex;
268/** Layered device handle for kernel keep-attached opens */
269static ldi_handle_t g_LdiHandle = NULL;
270/** Ref counting for IDCOpen calls */
271static uint64_t g_cLdiOpens = 0;
272/** The Mutex protecting the LDI handle in IDC opens */
273static kmutex_t g_LdiMtx;
274
275
276/******************************************************************************
277* Kernel entry points *
278******************************************************************************/
279
280/** Driver initialisation. */
281int _init(void)
282{
283 /*
284 * Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
285 */
286 int rc = RTR0Init(0);
287 if (RT_SUCCESS(rc))
288 {
289 PRTLOGGER pRelLogger;
290 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
291 modctl_t *pModCtl;
292
293 rc = RTLogCreate(&pRelLogger, 0 /* fFlags */, "all",
294 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
295 RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER
296 | RTLOGDEST_USER, NULL);
297 if (RT_SUCCESS(rc))
298 RTLogRelSetDefaultInstance(pRelLogger);
299 else
300 cmn_err(CE_NOTE, "failed to initialize driver logging rc=%d!\n", rc);
301
302 /*
303 * Prevent module autounloading.
304 */
305 pModCtl = mod_getctl(&g_vbgr0SolModLinkage);
306 if (pModCtl)
307 pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
308 else
309 LogRel((DEVICE_NAME ":failed to disable autounloading!\n"));
310 rc = mod_install(&g_vbgr0SolModLinkage);
311 }
312 else
313 {
314 cmn_err(CE_NOTE, "_init: RTR0Init failed. rc=%d\n", rc);
315 return EINVAL;
316 }
317
318 LogRel((DEVICE_NAME ": initialisation returning %d.\n", rc));
319 return rc;
320}
321
322
323#ifdef TESTCASE
324/** Simple test of the flow through _init. */
325static void test_init(RTTEST hTest)
326{
327 RTTestSub(hTest, "Testing _init");
328 RTTEST_CHECK(hTest, _init() == 0);
329}
330#endif
331
332
333/** Driver cleanup. */
334int _fini(void)
335{
336 int rc;
337
338 LogRelFlow((DEVICE_NAME ":_fini\n"));
339 rc = mod_remove(&g_vbgr0SolModLinkage);
340 mutex_destroy(&g_StateMutex);
341
342 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
343 RTLogDestroy(RTLogSetDefaultInstance(NULL));
344
345 RTR0Term();
346 return rc;
347}
348
349
350/** Driver identification. */
351int _info(struct modinfo *pModInfo)
352{
353 LogFlow((DEVICE_NAME ":_info\n"));
354 return mod_info(&g_vbgr0SolModLinkage, pModInfo);
355}
356
357
358/******************************************************************************
359* Helper routines *
360******************************************************************************/
361
362/** Calls the kernel IOCtl to report mouse status to the host on behalf of
363 * an open session. */
364static int vbgr0SolSetMouseStatus(PVBOXGUESTSESSION pSession, uint32_t fStatus)
365{
366 return VBoxGuestCommonIOCtl(VBOXGUEST_IOCTL_SET_MOUSE_STATUS, &g_DevExt,
367 pSession, &fStatus, sizeof(fStatus), NULL);
368}
369
370/** Resets (zeroes) a member in our open node state array in an IRQ-safe way.
371 */
372static void vbgr0SolResetSoftState(PVBGR0STATE pState)
373{
374 mutex_enter(&g_StateMutex);
375 RT_ZERO(*pState);
376 mutex_exit(&g_StateMutex);
377}
378
379/******************************************************************************
380* Main code *
381******************************************************************************/
382
383/**
384 * Open callback for the read queue, which we use as a generic device open
385 * handler.
386 */
387int vbgr0SolOpen(queue_t *pReadQueue, dev_t *pDev, int fFlag, int fMode,
388 cred_t *pCred)
389{
390 int rc;
391 PVBOXGUESTSESSION pSession = NULL;
392 PVBGR0STATE pState = NULL;
393 unsigned cInstance;
394
395 NOREF(fFlag);
396 NOREF(pCred);
397 LogRelFlow((DEVICE_NAME "::Open\n"));
398
399 /*
400 * Sanity check on the mode parameter - only open as a driver, not a
401 * module, and we do cloning ourselves. Note that we start at 1, as minor
402 * zero was allocated to the file system device node in vbgr0SolAttach
403 * (see https://blogs.oracle.com/timatworkhomeandinbetween/entry/using_makedevice_in_a_drivers).
404 */
405 if (fMode)
406 {
407 LogRelFlow((DEVICE_NAME "::Open: invalid attempt to clone device."));
408 return EINVAL;
409 }
410
411 for (cInstance = 1; cInstance < MAX_OPEN_NODES; cInstance++)
412 {
413 if (ASMAtomicCmpXchgPtr(&g_aOpenNodeStates[cInstance].pWriteQueue,
414 WR(pReadQueue), NULL))
415 {
416 pState = &g_aOpenNodeStates[cInstance];
417 break;
418 }
419 }
420 if (!pState)
421 {
422 LogRelFlow((DEVICE_NAME "::Open: too many open instances."));
423 return ENXIO;
424 }
425
426 /*
427 * Create a new session.
428 */
429 rc = VBoxGuestCreateUserSession(&g_DevExt, &pSession);
430 if (RT_SUCCESS(rc))
431 {
432 pState->pSession = pSession;
433 *pDev = makedevice(getmajor(*pDev), cInstance);
434 /* Initialise user data for the queues to our state and vice-versa. */
435 WR(pReadQueue)->q_ptr = (char *)pState;
436 pReadQueue->q_ptr = (char *)pState;
437 qprocson(pReadQueue);
438 LogRel((DEVICE_NAME "::Open: pSession=%p pState=%p pid=%d\n", pSession, pState, (int)RTProcSelf()));
439 return 0;
440 }
441
442 /* Failed, clean up. */
443 vbgr0SolResetSoftState(pState);
444
445 LogRel((DEVICE_NAME "::Open: VBoxGuestCreateUserSession failed. rc=%d\n", rc));
446 return EFAULT;
447}
448
449
450/**
451 * Close callback for the read queue, which we use as a generic device close
452 * handler.
453 */
454int vbgr0SolClose(queue_t *pReadQueue, int fFlag, cred_t *pCred)
455{
456 PVBOXGUESTSESSION pSession = NULL;
457 PVBGR0STATE pState = (PVBGR0STATE)pReadQueue->q_ptr;
458
459 LogFlow((DEVICE_NAME "::Close pid=%d\n", (int)RTProcSelf()));
460 NOREF(fFlag);
461 NOREF(pCred);
462
463 if (!pState)
464 {
465 Log((DEVICE_NAME "::Close: failed to get pState.\n"));
466 return EFAULT;
467 }
468 qprocsoff(pReadQueue);
469 pSession = pState->pSession;
470 vbgr0SolResetSoftState(pState);
471 pReadQueue->q_ptr = NULL;
472
473 Log((DEVICE_NAME "::Close: pSession=%p pState=%p\n", pSession, pState));
474 if (!pSession)
475 {
476 Log((DEVICE_NAME "::Close: failed to get pSession.\n"));
477 return EFAULT;
478 }
479
480 /*
481 * Close the session.
482 */
483 VBoxGuestCloseSession(&g_DevExt, pSession);
484 return 0;
485}
486
487
488#ifdef TESTCASE
489/** Simple test of vbgr0SolOpen and vbgr0SolClose. */
490static void testOpenClose(RTTEST hTest)
491{
492 queue_t aQueues[4];
493 dev_t device = 0;
494 int rc;
495
496 RTTestSub(hTest, "Testing vbgr0SolOpen and vbgr0SolClose");
497 RT_ZERO(g_aOpenNodeStates);
498 RT_ZERO(aQueues);
499 doInitQueues(&aQueues[0]);
500 doInitQueues(&aQueues[2]);
501 rc = vbgr0SolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
502 RTTEST_CHECK(hTest, rc == 0);
503 RTTEST_CHECK(hTest, g_aOpenNodeStates[1].pWriteQueue == WR(&aQueues[0]));
504 rc = vbgr0SolOpen(RD(&aQueues[2]), &device, 0, 0, NULL);
505 RTTEST_CHECK(hTest, rc == 0);
506 RTTEST_CHECK(hTest, g_aOpenNodeStates[2].pWriteQueue == WR(&aQueues[2]));
507 vbgr0SolClose(RD(&aQueues[0]), 0, NULL);
508 vbgr0SolClose(RD(&aQueues[1]), 0, NULL);
509}
510#endif
511
512
513/* Helper for vbgr0SolWPut. */
514static int vbgr0SolDispatchIOCtl(queue_t *pWriteQueue, mblk_t *pMBlk);
515
516/**
517 * Handler for messages sent from above (user-space and upper modules) which
518 * land in our write queue.
519 */
520int vbgr0SolWPut(queue_t *pWriteQueue, mblk_t *pMBlk)
521{
522 LogRelFlowFunc((DEVICE_NAME "::\n"));
523 switch (pMBlk->b_datap->db_type)
524 {
525 case M_FLUSH:
526 /* Flush the write queue if so requested. */
527 if (*pMBlk->b_rptr & FLUSHW)
528 flushq(pWriteQueue, FLUSHDATA);
529
530 /* Flush the read queue if so requested. */
531 if (*pMBlk->b_rptr & FLUSHR)
532 flushq(RD(pWriteQueue), FLUSHDATA);
533
534 /* We have no one below us to pass the message on to. */
535 return 0;
536 /* M_IOCDATA is additional data attached to (at least) transparent
537 * IOCtls. We handle the two together here and separate them further
538 * down. */
539 case M_IOCTL:
540 case M_IOCDATA:
541 {
542 int err = vbgr0SolDispatchIOCtl(pWriteQueue, pMBlk);
543 if (!err)
544 qreply(pWriteQueue, pMBlk);
545 else
546 miocnak(pWriteQueue, pMBlk, 0, err);
547 break;
548 }
549 }
550 return 0;
551}
552
553
554#ifdef TESTCASE
555/* Constants, definitions and test functions for testWPut. */
556static const int g_ccTestWPutFirmEvent = VUID_FIRM_EVENT;
557# define PVGFORMAT (&g_ccTestWPutFirmEvent)
558# define CBGFORMAT (sizeof(g_ccTestWPutFirmEvent))
559static const Ms_screen_resolution g_TestResolution = { 640, 480 };
560# define PMSIOSRES (&g_TestResolution)
561# define CBMSIOSRES (sizeof(g_TestResolution))
562
563static inline bool testSetResolution(RTTEST hTest, queue_t *pWriteQueue,
564 struct msgb *pMBlk)
565{
566 PVBGR0STATE pState = (PVBGR0STATE)pWriteQueue->q_ptr;
567 RTTEST_CHECK_MSG_RET(hTest, pState->cMaxScreenX
568 == g_TestResolution.width - 1,
569 (hTest, "pState->cMaxScreenX=%d\n",
570 pState->cMaxScreenX), false);
571 RTTEST_CHECK_MSG_RET(hTest, pState->cMaxScreenY
572 == g_TestResolution.height - 1,
573 (hTest, "pState->cMaxScreenY=%d\n",
574 pState->cMaxScreenY), false);
575 return true;
576}
577
578/** Data table for testWPut. */
579static struct
580{
581 int iIOCCmd;
582 size_t cbData;
583 const void *pvDataIn;
584 size_t cbDataIn;
585 const void *pvDataOut;
586 size_t cbDataOut;
587 int rcExp;
588 bool (*pfnExtra)(RTTEST hTest, queue_t *pWriteQueue, struct msgb *pMBlk);
589 bool fCanTransparent;
590} g_asTestWPut[] =
591{
592 /* iIOCCmd cbData pvDataIn cbDataIn
593 pvDataOut cbDataOut rcExp pfnExtra fCanTransparent */
594 { VUIDGFORMAT, sizeof(int), NULL, 0,
595 PVGFORMAT, CBGFORMAT, 0, NULL, true },
596 { VUIDGFORMAT, sizeof(int) - 1, NULL, 0,
597 NULL, 0, EINVAL, NULL, false },
598 { VUIDGFORMAT, sizeof(int) + 1, NULL, 0,
599 PVGFORMAT, CBGFORMAT, 0, NULL, true },
600 { VUIDSFORMAT, sizeof(int), PVGFORMAT, CBGFORMAT,
601 NULL, 0, 0, NULL, true },
602 { MSIOSRESOLUTION, CBMSIOSRES, PMSIOSRES, CBMSIOSRES,
603 NULL, 0, 0, testSetResolution, true },
604 { VUIDGWHEELINFO, 0, NULL, 0,
605 NULL, 0, EINVAL, NULL, true }
606};
607
608# undef PVGFORMAT
609# undef CBGFORMAT
610# undef PMSIOSRES
611# undef CBMSIOSRES
612
613/* Helpers for testWPut. */
614static void testWPutStreams(RTTEST hTest, unsigned i);
615static void testWPutTransparent(RTTEST hTest, unsigned i);
616static void testWPutIOCDataIn(RTTEST hTest, unsigned i);
617static void testWPutIOCDataOut(RTTEST hTest, unsigned i);
618
619/** Test WPut's handling of different IOCtls, which is bulk of the logic in
620 * this file. */
621static void testWPut(RTTEST hTest)
622{
623 unsigned i;
624
625 RTTestSub(hTest, "Testing vbgr0WPut");
626 for (i = 0; i < RT_ELEMENTS(g_asTestWPut); ++i)
627 {
628 AssertReturnVoid(g_asTestWPut[i].cbDataIn <= g_asTestWPut[i].cbData);
629 AssertReturnVoid(g_asTestWPut[i].cbDataOut <= g_asTestWPut[i].cbData);
630 testWPutStreams(hTest, i);
631 if (g_asTestWPut[i].fCanTransparent)
632 testWPutTransparent(hTest, i);
633 if (g_asTestWPut[i].fCanTransparent && g_asTestWPut[i].cbDataIn)
634 testWPutIOCDataIn(hTest, i);
635 if (g_asTestWPut[i].fCanTransparent && g_asTestWPut[i].cbDataOut)
636 testWPutIOCDataOut(hTest, i);
637 }
638}
639
640
641#define MSG_DATA_SIZE 1024
642
643/** Simulate sending a streams IOCtl to WPut with the parameters from table
644 * line @a i. */
645void testWPutStreams(RTTEST hTest, unsigned i)
646{
647 queue_t aQueues[2];
648 dev_t device = 0;
649 struct msgb *pMBlk = allocb(sizeof(struct iocblk), BPRI_MED);
650 struct msgb *pMBlkCont = allocb(MSG_DATA_SIZE, BPRI_MED);
651 struct iocblk *pIOCBlk = pMBlk ? (struct iocblk *)pMBlk->b_rptr : NULL;
652 int rc, cFormat = 0;
653
654 AssertReturnVoid(pMBlk);
655 AssertReturnVoidStmt(pMBlkCont, freemsg(pMBlk));
656 RT_ZERO(aQueues);
657 doInitQueues(&aQueues[0]);
658 rc = vbgr0SolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
659 RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
660 RTTEST_CHECK_MSG(hTest, g_aOpenNodeStates[1].pWriteQueue
661 == WR(&aQueues[0]), (hTest, "i=%u\n", i));
662 pMBlk->b_datap->db_type = M_IOCTL;
663 pIOCBlk->ioc_cmd = g_asTestWPut[i].iIOCCmd;
664 pIOCBlk->ioc_count = g_asTestWPut[i].cbData;
665 AssertReturnVoid(g_asTestWPut[i].cbData <= MSG_DATA_SIZE);
666 memcpy(pMBlkCont->b_rptr, g_asTestWPut[i].pvDataIn,
667 g_asTestWPut[i].cbDataIn);
668 pMBlk->b_cont = pMBlkCont;
669 rc = vbgr0SolWPut(WR(&aQueues[0]), pMBlk);
670 RTTEST_CHECK_MSG(hTest, pIOCBlk->ioc_error == g_asTestWPut[i].rcExp,
671 (hTest, "i=%u, IOCBlk.ioc_error=%d\n", i,
672 pIOCBlk->ioc_error));
673 RTTEST_CHECK_MSG(hTest, pIOCBlk->ioc_count == g_asTestWPut[i].cbDataOut,
674 (hTest, "i=%u, ioc_count=%u\n", i, pIOCBlk->ioc_count));
675 RTTEST_CHECK_MSG(hTest, !memcmp(pMBlkCont->b_rptr,
676 g_asTestWPut[i].pvDataOut,
677 g_asTestWPut[i].cbDataOut),
678 (hTest, "i=%u\n", i));
679 /* Hack to ensure that miocpullup() gets called when needed. */
680 if (g_asTestWPut[i].cbData > 0)
681 RTTEST_CHECK_MSG(hTest, pMBlk->b_flag == 1, (hTest, "i=%u\n", i));
682 if (!g_asTestWPut[i].rcExp)
683 RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
684 (hTest, "i=%u\n", i));
685 if (g_asTestWPut[i].pfnExtra)
686 if (!g_asTestWPut[i].pfnExtra(hTest, WR(&aQueues[0]), pMBlk))
687 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Called from %s.\n",
688 __PRETTY_FUNCTION__);
689 vbgr0SolClose(RD(&aQueues[1]), 0, NULL);
690 freemsg(pMBlk);
691}
692
693
694#define USER_ADDRESS 0xfeedbacc
695
696/** Simulate sending a transparent IOCtl to WPut with the parameters from table
697 * line @a i. */
698void testWPutTransparent(RTTEST hTest, unsigned i)
699{
700 queue_t aQueues[2];
701 dev_t device = 0;
702 struct msgb *pMBlk = allocb(sizeof(struct iocblk), BPRI_MED);
703 struct msgb *pMBlkCont = allocb(sizeof(void *), BPRI_MED);
704 struct iocblk *pIOCBlk = pMBlk ? (struct iocblk *)pMBlk->b_rptr : NULL;
705 struct copyreq *pCopyReq;
706 int rc, cFormat = 0;
707
708 /* if (g_asTestWPut[i].cbDataIn == 0 && g_asTestWPut[i].cbDataOut != 0)
709 return; */ /* This case will be handled once the current ones work. */
710 AssertReturnVoid(pMBlk);
711 AssertReturnVoidStmt(pMBlkCont, freemsg(pMBlk));
712 RT_ZERO(aQueues);
713 doInitQueues(&aQueues[0]);
714 rc = vbgr0SolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
715 RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
716 RTTEST_CHECK_MSG(hTest, g_aOpenNodeStates[1].pWriteQueue
717 == WR(&aQueues[0]), (hTest, "i=%u\n", i));
718 pMBlk->b_datap->db_type = M_IOCTL;
719 pIOCBlk->ioc_cmd = g_asTestWPut[i].iIOCCmd;
720 pIOCBlk->ioc_count = TRANSPARENT;
721 *(void **)pMBlkCont->b_rptr = (void *)USER_ADDRESS;
722 pMBlk->b_cont = pMBlkCont;
723 rc = vbgr0SolWPut(WR(&aQueues[0]), pMBlk);
724 pCopyReq = (struct copyreq *)pMBlk->b_rptr;
725 RTTEST_CHECK_MSG(hTest, ( ( g_asTestWPut[i].cbDataIn
726 && (pMBlk->b_datap->db_type == M_COPYIN))
727 || ( g_asTestWPut[i].cbDataOut
728 && (pMBlk->b_datap->db_type == M_COPYOUT))
729 || ( (g_asTestWPut[i].rcExp == 0)
730 && pMBlk->b_datap->db_type == M_IOCACK)
731 || (pMBlk->b_datap->db_type == M_IOCNAK)),
732 (hTest, "i=%u, db_type=%u\n", i,
733 (unsigned) pMBlk->b_datap->db_type));
734 /* Our TRANSPARENT IOCtls can only return non-zero if they have no payload.
735 * Others should either return zero or be non-TRANSPARENT only. */
736 if (pMBlk->b_datap->db_type == M_IOCNAK)
737 RTTEST_CHECK_MSG(hTest, pIOCBlk->ioc_error == g_asTestWPut[i].rcExp,
738 (hTest, "i=%u, IOCBlk.ioc_error=%d\n", i,
739 pIOCBlk->ioc_error));
740 if (g_asTestWPut[i].cbData)
741 {
742 RTTEST_CHECK_MSG(hTest, pCopyReq->cq_addr == (char *)USER_ADDRESS,
743 (hTest, "i=%u, cq_addr=%p\n", i, pCopyReq->cq_addr));
744 RTTEST_CHECK_MSG( hTest, pCopyReq->cq_size
745 == g_asTestWPut[i].cbDataIn
746 ? g_asTestWPut[i].cbDataIn
747 : g_asTestWPut[i].cbDataOut,
748 (hTest, "i=%u, cq_size=%llu\n", i,
749 (unsigned long long)pCopyReq->cq_size));
750 }
751 /* Implementation detail - check that the private pointer is correctly
752 * set to the user address *for two direction IOCtls* or NULL otherwise. */
753 if (g_asTestWPut[i].cbDataIn && g_asTestWPut[i].cbDataOut)
754 RTTEST_CHECK_MSG(hTest, pCopyReq->cq_private == (mblk_t *)USER_ADDRESS,
755 (hTest, "i=%u, cq_private=%p\n", i,
756 pCopyReq->cq_private));
757 else if ( (pMBlk->b_datap->db_type == M_COPYIN)
758 || (pMBlk->b_datap->db_type == M_COPYOUT))
759 RTTEST_CHECK_MSG(hTest, !pCopyReq->cq_private,
760 (hTest, "i=%u, cq_private=%p\n", i,
761 pCopyReq->cq_private));
762 if (!g_asTestWPut[i].rcExp)
763 RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
764 (hTest, "i=%u\n", i));
765 if (g_asTestWPut[i].pfnExtra && !g_asTestWPut[i].cbData)
766 if (!g_asTestWPut[i].pfnExtra(hTest, WR(&aQueues[0]), pMBlk))
767 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Called from %s.\n",
768 __PRETTY_FUNCTION__);
769 vbgr0SolClose(RD(&aQueues[1]), 0, NULL);
770 freemsg(pMBlk);
771}
772
773
774/** Simulate sending follow-on IOCData messages to a transparent IOCtl to WPut
775 * with the parameters from table line @a i. */
776void testWPutIOCDataIn(RTTEST hTest, unsigned i)
777{
778 queue_t aQueues[2];
779 dev_t device = 0;
780 struct msgb *pMBlk = allocb(sizeof(struct copyresp), BPRI_MED);
781 struct msgb *pMBlkCont = allocb(MSG_DATA_SIZE, BPRI_MED);
782 struct copyresp *pCopyResp = pMBlk ? (struct copyresp *)pMBlk->b_rptr
783 : NULL;
784 void *pvData = pMBlkCont ? pMBlkCont->b_rptr : NULL;
785 struct copyreq *pCopyReq;
786 int rc, cFormat = 0;
787
788 AssertReturnVoid(pMBlk);
789 AssertReturnVoidStmt(pMBlkCont, freemsg(pMBlk));
790 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "%s: i=%u\n", __PRETTY_FUNCTION__,
791 i);
792 AssertReturnVoidStmt(g_asTestWPut[i].cbDataIn, freemsg(pMBlk));
793 RT_ZERO(aQueues);
794 doInitQueues(&aQueues[0]);
795 rc = vbgr0SolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
796 RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
797 RTTEST_CHECK_MSG(hTest, g_aOpenNodeStates[1].pWriteQueue
798 == WR(&aQueues[0]), (hTest, "i=%u\n", i));
799 pMBlk->b_datap->db_type = M_IOCDATA;
800 pCopyResp->cp_cmd = g_asTestWPut[i].iIOCCmd;
801 if (g_asTestWPut[i].cbDataOut)
802 pCopyResp->cp_private = USER_ADDRESS;
803 AssertReturnVoid(g_asTestWPut[i].cbData <= MSG_DATA_SIZE);
804 memcpy(pMBlkCont->b_rptr, g_asTestWPut[i].pvDataIn, g_asTestWPut[i].cbDataIn);
805 pMBlk->b_cont = pMBlkCont;
806 rc = vbgr0SolWPut(WR(&aQueues[0]), pMBlk);
807 pCopyReq = (struct copyreq *)pMBlk->b_rptr;
808 RTTEST_CHECK_MSG(hTest, ( ( g_asTestWPut[i].cbDataOut
809 && (pMBlk->b_datap->db_type == M_COPYOUT))
810 || ( (g_asTestWPut[i].rcExp == 0)
811 && pMBlk->b_datap->db_type == M_IOCACK)
812 || (pMBlk->b_datap->db_type == M_IOCNAK)),
813 (hTest, "i=%u, db_type=%u\n", i,
814 (unsigned) pMBlk->b_datap->db_type));
815 if (g_asTestWPut[i].cbDataOut)
816 {
817 RTTEST_CHECK_MSG(hTest, pCopyReq->cq_addr == (char *)pvData,
818 (hTest, "i=%u, cq_addr=%p\n", i, pCopyReq->cq_addr));
819 RTTEST_CHECK_MSG(hTest, pCopyReq->cq_size == g_asTestWPut[i].cbData,
820 (hTest, "i=%u, cq_size=%llu\n", i,
821 (unsigned long long)pCopyReq->cq_size));
822 RTTEST_CHECK_MSG(hTest, !memcmp(pvData, g_asTestWPut[i].pvDataOut,
823 g_asTestWPut[i].cbDataOut),
824 (hTest, "i=%u\n", i));
825 }
826 RTTEST_CHECK_MSG(hTest, !pCopyReq->cq_private,
827 (hTest, "i=%u, cq_private=%p\n", i,
828 pCopyReq->cq_private));
829 if (!g_asTestWPut[i].rcExp)
830 RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
831 (hTest, "i=%u\n", i));
832 if (g_asTestWPut[i].pfnExtra && !g_asTestWPut[i].cbDataOut)
833 if (!g_asTestWPut[i].pfnExtra(hTest, WR(&aQueues[0]), pMBlk))
834 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Called from %s.\n",
835 __PRETTY_FUNCTION__);
836 vbgr0SolClose(RD(&aQueues[1]), 0, NULL);
837 freemsg(pMBlk);
838}
839
840
841/** Simulate sending follow-on IOCData messages to a transparent IOCtl to WPut
842 * with the parameters from table line @a i. */
843void testWPutIOCDataOut(RTTEST hTest, unsigned i)
844{
845 queue_t aQueues[2];
846 dev_t device = 0;
847 struct msgb *pMBlk = allocb(sizeof(struct copyresp), BPRI_MED);
848 struct copyresp *pCopyResp = pMBlk ? (struct copyresp *)pMBlk->b_rptr
849 : NULL;
850 int rc, cFormat = 0;
851
852 AssertReturnVoid(pMBlk);
853 AssertReturnVoidStmt(g_asTestWPut[i].cbDataOut, freemsg(pMBlk));
854 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "%s: i=%u\n", __PRETTY_FUNCTION__,
855 i);
856 RT_ZERO(aQueues);
857 doInitQueues(&aQueues[0]);
858 rc = vbgr0SolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
859 RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
860 RTTEST_CHECK_MSG(hTest, g_aOpenNodeStates[1].pWriteQueue
861 == WR(&aQueues[0]), (hTest, "i=%u\n", i));
862 pMBlk->b_datap->db_type = M_IOCDATA;
863 pCopyResp->cp_cmd = g_asTestWPut[i].iIOCCmd;
864 rc = vbgr0SolWPut(WR(&aQueues[0]), pMBlk);
865 RTTEST_CHECK_MSG(hTest, pMBlk->b_datap->db_type == M_IOCACK,
866 (hTest, "i=%u, db_type=%u\n", i,
867 (unsigned) pMBlk->b_datap->db_type));
868 if (!g_asTestWPut[i].rcExp)
869 RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
870 (hTest, "i=%u\n", i));
871 vbgr0SolClose(RD(&aQueues[1]), 0, NULL);
872 freemsg(pMBlk);
873}
874#endif
875
876
877/** Data transfer direction of an IOCtl. This is used for describing
878 * transparent IOCtls, and @a UNSPECIFIED is not a valid value for them. */
879enum IOCTLDIRECTION
880{
881 /** This IOCtl transfers no data. */
882 NONE,
883 /** This IOCtl only transfers data from user to kernel. */
884 IN,
885 /** This IOCtl only transfers data from kernel to user. */
886 OUT,
887 /** This IOCtl transfers data from user to kernel and back. */
888 BOTH,
889 /** We aren't saying anything about how the IOCtl transfers data. */
890 UNSPECIFIED
891};
892
893/**
894 * IOCtl handler function.
895 * @returns 0 on success, error code on failure.
896 * @param iCmd The IOCtl command number.
897 * @param pvData Buffer for the user data.
898 * @param cbBuffer Size of the buffer in @a pvData or zero.
899 * @param pcbData Where to set the size of the data returned. Required for
900 * handlers which return data.
901 * @param prc Where to store the return code. Default is zero. Only
902 * used for IOCtls without data for convenience of
903 * implemention.
904 */
905typedef int FNVBGR0SOLIOCTL(PVBGR0STATE pState, int iCmd, void *pvData,
906 size_t cbBuffer, size_t *pcbData, int *prc);
907typedef FNVBGR0SOLIOCTL *PFNVBGR0SOLIOCTL;
908
909/* Helpers for vbgr0SolDispatchIOCtl. */
910static int vbgr0SolHandleIOCtl(queue_t *pWriteQueue, mblk_t *pMBlk,
911 PFNVBGR0SOLIOCTL pfnHandler,
912 int iCmd, size_t cbTransparent,
913 enum IOCTLDIRECTION enmDirection);
914static int vbgr0SolVUIDIOCtl(PVBGR0STATE pState, int iCmd, void *pvData,
915 size_t cbBuffer, size_t *pcbData, int *prc);
916static int vbgr0SolGuestIOCtl(PVBGR0STATE pState, int iCmd, void *pvData,
917 size_t cbBuffer, size_t *pcbData, int *prc);
918
919/** Table of supported VUID IOCtls. */
920struct
921{
922 /** The IOCtl number. */
923 int iCmd;
924 /** The size of the buffer which needs to be copied between user and kernel
925 * space, or zero if unknown (must be known for tranparent IOCtls). */
926 size_t cbBuffer;
927 /** The direction the buffer data needs to be copied. This must be
928 * specified for transparent IOCtls. */
929 enum IOCTLDIRECTION enmDirection;
930} g_aVUIDIOCtlDescriptions[] =
931{
932 { VUIDGFORMAT, sizeof(int), OUT },
933 { VUIDSFORMAT, sizeof(int), IN },
934 { VUIDGADDR, 0, UNSPECIFIED },
935 { VUIDGADDR, 0, UNSPECIFIED },
936 { MSIOGETPARMS, sizeof(Ms_parms), OUT },
937 { MSIOSETPARMS, sizeof(Ms_parms), IN },
938 { MSIOSRESOLUTION, sizeof(Ms_screen_resolution), IN },
939 { MSIOBUTTONS, sizeof(int), OUT },
940 { VUIDGWHEELCOUNT, sizeof(int), OUT },
941 { VUIDGWHEELINFO, 0, UNSPECIFIED },
942 { VUIDGWHEELSTATE, 0, UNSPECIFIED },
943 { VUIDSWHEELSTATE, 0, UNSPECIFIED }
944};
945
946/**
947 * Handle a STREAMS IOCtl message for our driver on the write stream. This
948 * function takes care of the IOCtl logic only and does not call qreply() or
949 * miocnak() at all - the caller must call these on success or failure
950 * respectively.
951 * @returns 0 on success or the IOCtl error code on failure.
952 * @param pWriteQueue pointer to the STREAMS write queue structure.
953 * @param pMBlk pointer to the STREAMS message block structure.
954 */
955static int vbgr0SolDispatchIOCtl(queue_t *pWriteQueue, mblk_t *pMBlk)
956{
957 struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
958 int iCmd = pIOCBlk->ioc_cmd, iCmdType = iCmd & (0xff << 8);
959 size_t cbBuffer;
960 enum IOCTLDIRECTION enmDirection;
961
962 LogFlowFunc((DEVICE_NAME "::iCmdType=%c, iCmd=%d\n",
963 (char) (iCmdType >> 8), iCmd));
964 switch (iCmdType)
965 {
966 case MSIOC:
967 case VUIOC:
968 {
969 unsigned i;
970
971 for (i = 0; i < RT_ELEMENTS(g_aVUIDIOCtlDescriptions); ++i)
972 if (g_aVUIDIOCtlDescriptions[i].iCmd == iCmd)
973 {
974 cbBuffer = g_aVUIDIOCtlDescriptions[i].cbBuffer;
975 enmDirection = g_aVUIDIOCtlDescriptions[i].enmDirection;
976 return vbgr0SolHandleIOCtl(pWriteQueue, pMBlk,
977 vbgr0SolVUIDIOCtl, iCmd,
978 cbBuffer, enmDirection);
979 }
980 return EINVAL;
981 }
982 case 'V' << 8:
983 return ENOTTY;
984 return vbgr0SolHandleIOCtl(pWriteQueue, pMBlk, vbgr0SolGuestIOCtl,
985 iCmd, 0, UNSPECIFIED);
986 default:
987 return ENOTTY;
988 }
989}
990
991
992/* Helpers for vbgr0SolHandleIOCtl. */
993static int vbgr0SolHandleIOCtlData(queue_t *pWriteQueue, mblk_t *pMBlk,
994 PFNVBGR0SOLIOCTL pfnHandler, int iCmd,
995 size_t cbTransparent,
996 enum IOCTLDIRECTION enmDirection);
997
998static int vbgr0SolHandleTransparentIOCtl(queue_t *pWriteQueue, mblk_t *pMBlk,
999 PFNVBGR0SOLIOCTL pfnHandler,
1000 int iCmd, size_t cbTransparent,
1001 enum IOCTLDIRECTION enmDirection);
1002
1003static int vbgr0SolHandleIStrIOCtl(queue_t *pWriteQueue, mblk_t *pMBlk,
1004 PFNVBGR0SOLIOCTL pfnHandler, int iCmd);
1005
1006/**
1007 * Generic code for handling STREAMS-specific IOCtl logic and boilerplate. It
1008 * calls the IOCtl handler passed to it without the handler having to be aware
1009 * of STREAMS structures, or whether this is a transparent (traditional) or an
1010 * I_STR (using a STREAMS structure to describe the data) IOCtl. With the
1011 * caveat that we only support transparent IOCtls which pass all data in a
1012 * single buffer of a fixed size (I_STR IOCtls are restricted to a single
1013 * buffer anyway, but the caller can choose the buffer size).
1014 * @returns 0 on success or the IOCtl error code on failure.
1015 * @param pWriteQueue pointer to the STREAMS write queue structure.
1016 * @param pMBlk pointer to the STREAMS message block structure.
1017 * @param pfnHandler pointer to the right IOCtl handler function for this
1018 * IOCtl number.
1019 * @param iCmd IOCtl command number.
1020 * @param cbTransparent size of the user space buffer for this IOCtl number,
1021 * used for processing transparent IOCtls. Pass zero
1022 * for IOCtls with no maximum buffer size (which will
1023 * not be able to be handled as transparent) or with
1024 * no argument.
1025 * @param enmDirection data transfer direction of the IOCtl.
1026 */
1027static int vbgr0SolHandleIOCtl(queue_t *pWriteQueue, mblk_t *pMBlk,
1028 PFNVBGR0SOLIOCTL pfnHandler, int iCmd,
1029 size_t cbTransparent,
1030 enum IOCTLDIRECTION enmDirection)
1031{
1032 struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
1033
1034 if (pMBlk->b_datap->db_type == M_IOCDATA)
1035 return vbgr0SolHandleIOCtlData(pWriteQueue, pMBlk, pfnHandler, iCmd,
1036 cbTransparent, enmDirection);
1037 else if ( pMBlk->b_datap->db_type == M_IOCTL
1038 && pIOCBlk->ioc_count == TRANSPARENT)
1039 return vbgr0SolHandleTransparentIOCtl(pWriteQueue, pMBlk, pfnHandler,
1040 iCmd, cbTransparent,
1041 enmDirection);
1042 else if (pMBlk->b_datap->db_type == M_IOCTL)
1043 return vbgr0SolHandleIStrIOCtl(pWriteQueue, pMBlk, pfnHandler, iCmd);
1044 return EINVAL;
1045}
1046
1047
1048/**
1049 * Helper for vbgr0SolHandleIOCtl. This rather complicated-looking
1050 * code is basically the standard boilerplate for handling any streams IOCtl
1051 * additional data, which we currently only use for transparent IOCtls.
1052 * @copydoc vbgr0SolHandleIOCtl
1053 */
1054static int vbgr0SolHandleIOCtlData(queue_t *pWriteQueue, mblk_t *pMBlk,
1055 PFNVBGR0SOLIOCTL pfnHandler, int iCmd,
1056 size_t cbTransparent,
1057 enum IOCTLDIRECTION enmDirection)
1058{
1059 struct copyresp *pCopyResp = (struct copyresp *)pMBlk->b_rptr;
1060 PVBGR0STATE pState = (PVBGR0STATE)pWriteQueue->q_ptr;
1061
1062 if (pCopyResp->cp_rval) /* cp_rval is a pointer used as a boolean. */
1063 {
1064 freemsg(pMBlk);
1065 return EAGAIN;
1066 }
1067 if ((pCopyResp->cp_private && enmDirection == BOTH) || enmDirection == IN)
1068 {
1069 size_t cbData = 0;
1070 void *pvData = NULL;
1071 int err;
1072
1073 if (!pMBlk->b_cont)
1074 return EINVAL;
1075 if (enmDirection == BOTH && !pCopyResp->cp_private)
1076 return EINVAL;
1077 pvData = pMBlk->b_cont->b_rptr;
1078 err = pfnHandler(pState, iCmd, pvData, cbTransparent, &cbData, NULL);
1079 if (!err && enmDirection == BOTH)
1080 mcopyout(pMBlk, NULL, cbData, pCopyResp->cp_private, NULL);
1081 else if (!err && enmDirection == IN)
1082 miocack(pWriteQueue, pMBlk, 0, 0);
1083 return err;
1084 }
1085 else
1086 {
1087 AssertReturn(enmDirection == OUT || enmDirection == BOTH, EINVAL);
1088 miocack(pWriteQueue, pMBlk, 0, 0);
1089 return 0;
1090 }
1091}
1092
1093/**
1094 * Helper for vbgr0SolHandleIOCtl. This rather complicated-looking
1095 * code is basically the standard boilerplate for handling transparent IOCtls,
1096 * that is, IOCtls which are not re-packed inside STREAMS IOCtls.
1097 * @copydoc vbgr0SolHandleIOCtl
1098 */
1099int vbgr0SolHandleTransparentIOCtl(queue_t *pWriteQueue, mblk_t *pMBlk,
1100 PFNVBGR0SOLIOCTL pfnHandler, int iCmd,
1101 size_t cbTransparent,
1102 enum IOCTLDIRECTION enmDirection)
1103{
1104 int err = 0, rc = 0;
1105 size_t cbData = 0;
1106 PVBGR0STATE pState = (PVBGR0STATE)pWriteQueue->q_ptr;
1107
1108 if ( (enmDirection != NONE && !pMBlk->b_cont)
1109 || enmDirection == UNSPECIFIED)
1110 return EINVAL;
1111 if (enmDirection == IN || enmDirection == BOTH)
1112 {
1113 void *pUserAddr = NULL;
1114 /* We only need state data if there is something to copy back. */
1115 if (enmDirection == BOTH)
1116 pUserAddr = *(void **)pMBlk->b_cont->b_rptr;
1117 mcopyin(pMBlk, pUserAddr /* state data */, cbTransparent, NULL);
1118 }
1119 else if (enmDirection == OUT)
1120 {
1121 mblk_t *pMBlkOut = allocb(cbTransparent, BPRI_MED);
1122 void *pvData;
1123
1124 if (!pMBlkOut)
1125 return EAGAIN;
1126 pvData = pMBlkOut->b_rptr;
1127 err = pfnHandler(pState, iCmd, pvData, cbTransparent, &cbData, NULL);
1128 if (!err)
1129 mcopyout(pMBlk, NULL, cbData, NULL, pMBlkOut);
1130 else
1131 freemsg(pMBlkOut);
1132 }
1133 else
1134 {
1135 AssertReturn(enmDirection == NONE, EINVAL);
1136 err = pfnHandler(pState, iCmd, NULL, 0, NULL, &rc);
1137 if (!err)
1138 miocack(pWriteQueue, pMBlk, 0, rc);
1139 }
1140 return err;
1141}
1142
1143/**
1144 * Helper for vbgr0SolHandleIOCtl. This rather complicated-looking
1145 * code is basically the standard boilerplate for handling any streams IOCtl.
1146 * @copydoc vbgr0SolHandleIOCtl
1147 */
1148static int vbgr0SolHandleIStrIOCtl(queue_t *pWriteQueue, mblk_t *pMBlk,
1149 PFNVBGR0SOLIOCTL pfnHandler, int iCmd)
1150{
1151 struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
1152 PVBGR0STATE pState = (PVBGR0STATE)pWriteQueue->q_ptr;
1153 uint_t cbBuffer = pIOCBlk->ioc_count;
1154 void *pvData = NULL;
1155 int err, rc = 0;
1156 size_t cbData = 0;
1157
1158 if (cbBuffer && !pMBlk->b_cont)
1159 return EINVAL;
1160 /* Repack the whole buffer into a single message block if needed. */
1161 if (cbBuffer)
1162 {
1163 err = miocpullup(pMBlk, cbBuffer);
1164 if (err)
1165 return err;
1166 pvData = pMBlk->b_cont->b_rptr;
1167 }
1168 err = pfnHandler(pState, iCmd, pvData, cbBuffer, &cbData, &rc);
1169 if (!err)
1170 miocack(pWriteQueue, pMBlk, cbData, rc);
1171 return err;
1172}
1173
1174
1175/**
1176 * Handle a VUID input device IOCtl.
1177 * @copydoc FNVBGR0SOLIOCTL
1178 */
1179static int vbgr0SolVUIDIOCtl(PVBGR0STATE pState, int iCmd, void *pvData,
1180 size_t cbBuffer, size_t *pcbData, int *prc)
1181{
1182 LogFlowFunc((DEVICE_NAME ":: " /* no '\n' */));
1183 switch (iCmd)
1184 {
1185 case VUIDGFORMAT:
1186 {
1187 LogFlowFunc(("VUIDGFORMAT\n"));
1188 if (cbBuffer < sizeof(int))
1189 return EINVAL;
1190 *(int *)pvData = VUID_FIRM_EVENT;
1191 *pcbData = sizeof(int);
1192 return 0;
1193 }
1194 case VUIDSFORMAT:
1195 LogFlowFunc(("VUIDSFORMAT\n"));
1196 /* We define our native format to be VUID_FIRM_EVENT, so there
1197 * is nothing more to do and we exit here on success or on
1198 * failure. */
1199 return 0;
1200 case VUIDGADDR:
1201 case VUIDSADDR:
1202 LogFlowFunc(("VUIDGADDR/VUIDSADDR\n"));
1203 return ENOTTY;
1204 case MSIOGETPARMS:
1205 {
1206 Ms_parms parms = { 0 };
1207
1208 LogFlowFunc(("MSIOGETPARMS\n"));
1209 if (cbBuffer < sizeof(Ms_parms))
1210 return EINVAL;
1211 *(Ms_parms *)pvData = parms;
1212 *pcbData = sizeof(Ms_parms);
1213 return 0;
1214 }
1215 case MSIOSETPARMS:
1216 LogFlowFunc(("MSIOSETPARMS\n"));
1217 return 0;
1218 case MSIOSRESOLUTION:
1219 {
1220 Ms_screen_resolution *pResolution = (Ms_screen_resolution *)pvData;
1221 int rc;
1222
1223 LogFlowFunc(("MSIOSRESOLUTION\n"));
1224 if (cbBuffer < sizeof(Ms_screen_resolution))
1225 return EINVAL;
1226 pState->cMaxScreenX = pResolution->width - 1;
1227 pState->cMaxScreenY = pResolution->height - 1;
1228 /* Note: we don't disable this again until session close. */
1229 rc = vbgr0SolSetMouseStatus(pState->pSession,
1230 VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE
1231 | VMMDEV_MOUSE_NEW_PROTOCOL);
1232 if (RT_SUCCESS(rc))
1233 return 0;
1234 pState->cMaxScreenX = 0;
1235 pState->cMaxScreenY = 0;
1236 return ENODEV;
1237 }
1238 case MSIOBUTTONS:
1239 {
1240 LogFlowFunc(("MSIOBUTTONS\n"));
1241 if (cbBuffer < sizeof(int))
1242 return EINVAL;
1243 *(int *)pvData = 0;
1244 *pcbData = sizeof(int);
1245 return 0;
1246 }
1247 case VUIDGWHEELCOUNT:
1248 {
1249 LogFlowFunc(("VUIDGWHEELCOUNT\n"));
1250 if (cbBuffer < sizeof(int))
1251 return EINVAL;
1252 *(int *)pvData = 0;
1253 *pcbData = sizeof(int);
1254 return 0;
1255 }
1256 case VUIDGWHEELINFO:
1257 case VUIDGWHEELSTATE:
1258 case VUIDSWHEELSTATE:
1259 LogFlowFunc(("VUIDGWHEELINFO/VUIDGWHEELSTATE/VUIDSWHEELSTATE\n"));
1260 return EINVAL;
1261 default:
1262 LogFlowFunc(("Invalid IOCtl command %x\n", iCmd));
1263 return EINVAL;
1264 }
1265}
1266
1267
1268/**
1269 * Handle a VBoxGuest IOCtl.
1270 * @copydoc FNVBGR0SOLIOCTL
1271 */
1272static int vbgr0SolGuestIOCtl(PVBGR0STATE pState, int iCmd, void *pvData,
1273 size_t cbBuffer, size_t *pcbData, int *prc)
1274{
1275 int rc = VBoxGuestCommonIOCtl(iCmd, &g_DevExt, pState->pSession, pvData, cbBuffer, pcbData);
1276 if (RT_SUCCESS(rc))
1277 {
1278 *prc = rc;
1279 return 0;
1280 }
1281 else
1282 {
1283 /*
1284 * We Log() instead of LogRel() here because VBOXGUEST_IOCTL_WAITEVENT can return VERR_TIMEOUT,
1285 * VBOXGUEST_IOCTL_CANCEL_ALL_EVENTS can return VERR_INTERRUPTED and possibly more in the future;
1286 * which are not really failures that require logging.
1287 */
1288 Log((DEVICE_NAME "::IOCtl: VBoxGuestCommonIOCtl failed. Cmd=%#x rc=%d\n", iCmd, rc));
1289 rc = RTErrConvertToErrno(rc);
1290 return rc;
1291 }
1292}
1293
1294
1295/**
1296 * Info entry point, called by solaris kernel for obtaining driver info.
1297 *
1298 * @param pDip The module structure instance (do not use).
1299 * @param enmCmd Information request type.
1300 * @param pvArg Type specific argument.
1301 * @param ppvResult Where to store the requested info.
1302 *
1303 * @return corresponding solaris error code.
1304 */
1305int vbgr0SolGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg,
1306 void **ppvResult)
1307{
1308 int rc = DDI_SUCCESS;
1309
1310 LogFlow((DEVICE_NAME "::GetInfo\n"));
1311 switch (enmCmd)
1312 {
1313 case DDI_INFO_DEVT2DEVINFO:
1314 *ppvResult = (void *)g_pDip;
1315 break;
1316
1317 case DDI_INFO_DEVT2INSTANCE:
1318 *ppvResult = (void *)(uintptr_t)ddi_get_instance(g_pDip);
1319 break;
1320
1321 default:
1322 rc = DDI_FAILURE;
1323 break;
1324 }
1325
1326 NOREF(pvArg);
1327 return rc;
1328}
1329
1330
1331/* Helpers for vbgr0SolAttach and vbgr0SolDetach. */
1332static int vbgr0SolAddIRQ(dev_info_t *pDip);
1333static void vbgr0SolRemoveIRQ(dev_info_t *pDip);
1334
1335/**
1336 * Attach entry point, to attach a device to the system or resume it.
1337 *
1338 * @param pDip The module structure instance.
1339 * @param enmCmd Attach type (ddi_attach_cmd_t)
1340 *
1341 * @return corresponding solaris error code.
1342 */
1343int vbgr0SolAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
1344{
1345 LogFlow((DEVICE_NAME "::Attach\n"));
1346 switch (enmCmd)
1347 {
1348 case DDI_ATTACH:
1349 {
1350 int instance, rc;
1351 ddi_acc_handle_t PciHandle;
1352
1353 if (g_pDip)
1354 {
1355 LogRel((DEVICE_NAME "::Attach: Only one instance supported.\n"));
1356 return DDI_FAILURE;
1357 }
1358 instance = ddi_get_instance(pDip);
1359
1360 /*
1361 * Enable resources for PCI access.
1362 */
1363 rc = pci_config_setup(pDip, &PciHandle);
1364 if (rc == DDI_SUCCESS)
1365 {
1366 /*
1367 * Map the register address space.
1368 */
1369 char *baseAddr; /* Actually caddr_t. */
1370 ddi_device_acc_attr_t deviceAttr;
1371 deviceAttr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
1372 deviceAttr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
1373 deviceAttr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
1374 deviceAttr.devacc_attr_access = DDI_DEFAULT_ACC;
1375 rc = ddi_regs_map_setup(pDip, 1, &baseAddr, 0, 0, &deviceAttr, &g_PciIOHandle);
1376 if (rc == DDI_SUCCESS)
1377 {
1378 /*
1379 * Read size of the MMIO region.
1380 */
1381 g_uIOPortBase = (uintptr_t)baseAddr;
1382 rc = ddi_dev_regsize(pDip, 2, &g_cbMMIO);
1383 if (rc == DDI_SUCCESS)
1384 {
1385 rc = ddi_regs_map_setup(pDip, 2, &g_pMMIOBase, 0, g_cbMMIO, &deviceAttr,
1386 &g_PciMMIOHandle);
1387 if (rc == DDI_SUCCESS)
1388 {
1389 /*
1390 * Add IRQ of VMMDev.
1391 */
1392 rc = vbgr0SolAddIRQ(pDip);
1393 if (rc == DDI_SUCCESS)
1394 {
1395 /*
1396 * Call the common device extension initializer.
1397 */
1398#if ARCH_BITS == 64
1399# define VBOXGUEST_OS_TYPE VBOXOSTYPE_Solaris_x64
1400#else
1401# define VBOXGUEST_OS_TYPE VBOXOSTYPE_Solaris
1402#endif
1403 rc = VBoxGuestInitDevExt(&g_DevExt,
1404 g_uIOPortBase,
1405 g_pMMIOBase, g_cbMMIO,
1406 VBOXGUEST_OS_TYPE,
1407 VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
1408#undef VBOXGUEST_OS_TYPE
1409 if (RT_SUCCESS(rc))
1410 {
1411 rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, instance, DDI_PSEUDO, 0);
1412 if (rc == DDI_SUCCESS)
1413 {
1414 g_pDip = pDip;
1415 pci_config_teardown(&PciHandle);
1416 return DDI_SUCCESS;
1417 }
1418
1419 LogRel((DEVICE_NAME "::Attach: ddi_create_minor_node failed.\n"));
1420 VBoxGuestDeleteDevExt(&g_DevExt);
1421 }
1422 else
1423 LogRel((DEVICE_NAME "::Attach: VBoxGuestInitDevExt failed.\n"));
1424 vbgr0SolRemoveIRQ(pDip);
1425 }
1426 else
1427 LogRel((DEVICE_NAME "::Attach: vbgr0SolAddIRQ failed.\n"));
1428 ddi_regs_map_free(&g_PciMMIOHandle);
1429 }
1430 else
1431 LogRel((DEVICE_NAME "::Attach: ddi_regs_map_setup for MMIO region failed.\n"));
1432 }
1433 else
1434 LogRel((DEVICE_NAME "::Attach: ddi_dev_regsize for MMIO region failed.\n"));
1435 ddi_regs_map_free(&g_PciIOHandle);
1436 }
1437 else
1438 LogRel((DEVICE_NAME "::Attach: ddi_regs_map_setup for IOport failed.\n"));
1439 pci_config_teardown(&PciHandle);
1440 }
1441 else
1442 LogRel((DEVICE_NAME "::Attach: pci_config_setup failed rc=%d.\n", rc));
1443 return DDI_FAILURE;
1444 }
1445
1446 case DDI_RESUME:
1447 {
1448 /** @todo implement resume for guest driver. */
1449 return DDI_SUCCESS;
1450 }
1451
1452 default:
1453 return DDI_FAILURE;
1454 }
1455}
1456
1457
1458/**
1459 * Detach entry point, to detach a device to the system or suspend it.
1460 *
1461 * @param pDip The module structure instance.
1462 * @param enmCmd Attach type (ddi_attach_cmd_t)
1463 *
1464 * @return corresponding solaris error code.
1465 */
1466int vbgr0SolDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
1467{
1468 LogFlow((DEVICE_NAME "::Detach\n"));
1469 switch (enmCmd)
1470 {
1471 case DDI_DETACH:
1472 {
1473 vbgr0SolRemoveIRQ(pDip);
1474 ddi_regs_map_free(&g_PciIOHandle);
1475 ddi_regs_map_free(&g_PciMMIOHandle);
1476 ddi_remove_minor_node(pDip, NULL);
1477 VBoxGuestDeleteDevExt(&g_DevExt);
1478 g_pDip = NULL;
1479 return DDI_SUCCESS;
1480 }
1481
1482 case DDI_SUSPEND:
1483 {
1484 /** @todo implement suspend for guest driver. */
1485 return DDI_SUCCESS;
1486 }
1487
1488 default:
1489 return DDI_FAILURE;
1490 }
1491}
1492
1493
1494/* Interrupt service routine installed by vbgr0SolAddIRQ. */
1495static uint_t vbgr0SolISR(char *Arg /* Actually caddr_t. */);
1496
1497/**
1498 * Sets IRQ for VMMDev.
1499 *
1500 * @returns Solaris error code.
1501 * @param pDip Pointer to the device info structure.
1502 */
1503static int vbgr0SolAddIRQ(dev_info_t *pDip)
1504{
1505 int IntrType = 0, rc;
1506
1507 LogFlow((DEVICE_NAME "::AddIRQ: pDip=%p\n", pDip));
1508 rc = ddi_intr_get_supported_types(pDip, &IntrType);
1509 if (rc == DDI_SUCCESS)
1510 {
1511 /* We won't need to bother about MSIs. */
1512 if (IntrType & DDI_INTR_TYPE_FIXED)
1513 {
1514 int IntrCount = 0;
1515 rc = ddi_intr_get_nintrs(pDip, IntrType, &IntrCount);
1516 if ( rc == DDI_SUCCESS
1517 && IntrCount > 0)
1518 {
1519 int IntrAvail = 0;
1520 rc = ddi_intr_get_navail(pDip, IntrType, &IntrAvail);
1521 if ( rc == DDI_SUCCESS
1522 && IntrAvail > 0)
1523 {
1524 /* Allocated kernel memory for the interrupt handles. The allocation size is stored internally. */
1525 g_pIntr = RTMemAlloc(IntrCount * sizeof(ddi_intr_handle_t));
1526 if (g_pIntr)
1527 {
1528 int IntrAllocated;
1529 unsigned i;
1530 rc = ddi_intr_alloc(pDip, g_pIntr, IntrType, 0, IntrCount, &IntrAllocated, DDI_INTR_ALLOC_NORMAL);
1531 if ( rc == DDI_SUCCESS
1532 && IntrAllocated > 0)
1533 {
1534 uint_t uIntrPriority;
1535 g_cIntrAllocated = IntrAllocated;
1536 rc = ddi_intr_get_pri(g_pIntr[0], &uIntrPriority);
1537 if (rc == DDI_SUCCESS)
1538 {
1539 /* Initialize the mutex. */
1540 mutex_init(&g_IrqMutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));
1541
1542 /* Initialise the node state mutex. This will
1543 * be taken in the ISR. */
1544 mutex_init(&g_StateMutex, NULL, MUTEX_DRIVER,
1545 DDI_INTR_PRI(uIntrPriority));
1546 /* Assign interrupt handler functions and enable interrupts. */
1547 for (i = 0; i < IntrAllocated; i++)
1548 {
1549 rc = ddi_intr_add_handler(g_pIntr[i], (ddi_intr_handler_t *)vbgr0SolISR,
1550 NULL /* No Private Data */, NULL);
1551 if (rc == DDI_SUCCESS)
1552 rc = ddi_intr_enable(g_pIntr[i]);
1553 if (rc != DDI_SUCCESS)
1554 {
1555 /* Changing local IntrAllocated to hold so-far allocated handles for freeing. */
1556 IntrAllocated = i;
1557 break;
1558 }
1559 }
1560 if (rc == DDI_SUCCESS)
1561 return rc;
1562
1563 /* Remove any assigned handlers */
1564 LogRel((DEVICE_NAME ":failed to assign IRQs allocated=%d\n", IntrAllocated));
1565 for (i = 0; i < IntrAllocated; i++)
1566 ddi_intr_remove_handler(g_pIntr[i]);
1567 }
1568 else
1569 LogRel((DEVICE_NAME "::AddIRQ: failed to get priority of interrupt. rc=%d\n", rc));
1570
1571 /* Remove allocated IRQs, too bad we can free only one handle at a time. */
1572 for (i = 0; i < g_cIntrAllocated; i++)
1573 ddi_intr_free(g_pIntr[i]);
1574 }
1575 else
1576 LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", IntrCount));
1577 RTMemFree(g_pIntr);
1578 }
1579 else
1580 LogRel((DEVICE_NAME "::AddIRQ: failed to allocated IRQs. count=%d\n", IntrCount));
1581 }
1582 else
1583 LogRel((DEVICE_NAME "::AddIRQ: failed to get or insufficient available IRQs. rc=%d IntrAvail=%d\n", rc, IntrAvail));
1584 }
1585 else
1586 LogRel((DEVICE_NAME "::AddIRQ: failed to get or insufficient number of IRQs. rc=%d IntrCount=%d\n", rc, IntrCount));
1587 }
1588 else
1589 LogRel((DEVICE_NAME "::AddIRQ: invalid irq type. IntrType=%#x\n", IntrType));
1590 }
1591 else
1592 LogRel((DEVICE_NAME "::AddIRQ: failed to get supported interrupt types\n"));
1593 return rc;
1594}
1595
1596
1597/**
1598 * Removes IRQ for VMMDev.
1599 *
1600 * @param pDip Pointer to the device info structure.
1601 */
1602static void vbgr0SolRemoveIRQ(dev_info_t *pDip)
1603{
1604 unsigned i;
1605
1606 LogFlow((DEVICE_NAME "::RemoveIRQ:\n"));
1607 for (i = 0; i < g_cIntrAllocated; i++)
1608 {
1609 int rc = ddi_intr_disable(g_pIntr[i]);
1610 if (rc == DDI_SUCCESS)
1611 {
1612 rc = ddi_intr_remove_handler(g_pIntr[i]);
1613 if (rc == DDI_SUCCESS)
1614 ddi_intr_free(g_pIntr[i]);
1615 }
1616 }
1617 RTMemFree(g_pIntr);
1618 mutex_destroy(&g_IrqMutex);
1619}
1620
1621
1622/**
1623 * Interrupt Service Routine for VMMDev.
1624 *
1625 * @param Arg Private data (unused, will be NULL).
1626 * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
1627 */
1628static uint_t vbgr0SolISR(char *Arg /* Actually caddr_t. */)
1629{
1630 bool fOurIRQ;
1631
1632 LogFlow((DEVICE_NAME "::ISR:\n"));
1633 mutex_enter(&g_IrqMutex);
1634 fOurIRQ = VBoxGuestCommonISR(&g_DevExt);
1635 mutex_exit(&g_IrqMutex);
1636 return fOurIRQ ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED;
1637}
1638
1639
1640/* Helper for VBoxGuestNativeISRMousePollEvent. */
1641static void vbgr0SolVUIDPutAbsEvent(PVBGR0STATE pState, ushort_t cEvent,
1642 int cValue);
1643
1644/**
1645 * Native part of the IRQ service routine, called when the VBoxGuest mouse
1646 * pointer is moved. We send a VUID event up to user space.
1647 */
1648void VBoxGuestNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt)
1649{
1650 VMMDevReqMouseStatus *pReq;
1651 int rc;
1652 LogFlow((DEVICE_NAME "::NativeISRMousePollEvent:\n"));
1653
1654 rc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, sizeof(*pReq),
1655 VMMDevReq_GetMouseStatus);
1656 if (RT_FAILURE(rc))
1657 return; /* If kernel memory is short a missed event is acceptable! */
1658 pReq->mouseFeatures = 0;
1659 pReq->pointerXPos = 0;
1660 pReq->pointerYPos = 0;
1661 rc = VbglGRPerform(&pReq->header);
1662 if (RT_SUCCESS(rc))
1663 {
1664 unsigned i;
1665
1666 mutex_enter(&g_StateMutex);
1667 for (i = 1; i < MAX_OPEN_NODES; ++i)
1668 {
1669 int cMaxScreenX = g_aOpenNodeStates[i].cMaxScreenX;
1670 int cMaxScreenY = g_aOpenNodeStates[i].cMaxScreenY;
1671
1672 if (!cMaxScreenX || !cMaxScreenY)
1673 continue;
1674 vbgr0SolVUIDPutAbsEvent(&g_aOpenNodeStates[i], LOC_X_ABSOLUTE,
1675 pReq->pointerXPos * cMaxScreenX
1676 / VMMDEV_MOUSE_RANGE_MAX);
1677 vbgr0SolVUIDPutAbsEvent(&g_aOpenNodeStates[i], LOC_Y_ABSOLUTE,
1678 pReq->pointerYPos * cMaxScreenY
1679 / VMMDEV_MOUSE_RANGE_MAX);
1680 }
1681 mutex_exit(&g_StateMutex);
1682 }
1683 VbglGRFree(&pReq->header);
1684}
1685
1686
1687void vbgr0SolVUIDPutAbsEvent(PVBGR0STATE pState, ushort_t cEvent,
1688 int cValue)
1689{
1690 queue_t *pReadQueue = RD(pState->pWriteQueue);
1691 mblk_t *pMBlk = allocb(sizeof(Firm_event), BPRI_HI);
1692 Firm_event *pEvent;
1693 AssertReturnVoid(cEvent == LOC_X_ABSOLUTE || cEvent == LOC_Y_ABSOLUTE);
1694 if (!pMBlk)
1695 return; /* If kernel memory is short a missed event is acceptable! */
1696 pEvent = (Firm_event *)pMBlk->b_wptr;
1697 pEvent->id = cEvent;
1698 pEvent->pair_type = FE_PAIR_DELTA;
1699 pEvent->pair = cEvent == LOC_X_ABSOLUTE ? LOC_X_DELTA : LOC_Y_DELTA;
1700 pEvent->value = cValue;
1701 uniqtime32(&pEvent->time);
1702 pMBlk->b_wptr += sizeof(Firm_event);
1703 /* Put the message on the queue immediately if it is not blocked. */
1704 if (canput(pReadQueue->q_next))
1705 putnext(pReadQueue, pMBlk);
1706 else
1707 putbq(pReadQueue, pMBlk);
1708}
1709
1710
1711/* Common code that depends on g_DevExt. */
1712#ifndef TESTCASE
1713# include "VBoxGuestIDC-unix.c.h"
1714#endif
1715
1716#ifdef TESTCASE
1717int main(void)
1718{
1719 RTTEST hTest;
1720 int rc = RTTestInitAndCreate("tstVBoxGuest-solaris", &hTest);
1721 if (rc)
1722 return rc;
1723 RTTestBanner(hTest);
1724 test_init(hTest);
1725 testOpenClose(hTest);
1726 testWPut(hTest);
1727
1728 /*
1729 * Summary.
1730 */
1731 return RTTestSummaryAndDestroy(hTest);
1732}
1733#endif
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