VirtualBox

source: vbox/trunk/src/VBox/Additions/solaris/Mouse/vboxms.c@ 93115

Last change on this file since 93115 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.5 KB
Line 
1/* $Id: vboxms.c 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * VirtualBox Guest Additions Mouse Driver for Solaris.
4 */
5
6/*
7 * Copyright (C) 2012-2022 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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP LOG_GROUP_DRV_MOUSE
32#include <VBox/VMMDev.h>
33#include <VBox/VBoxGuestLib.h>
34#include <VBox/log.h>
35#include <VBox/version.h>
36#include <iprt/assert.h>
37#include <iprt/asm.h>
38
39#ifndef TESTCASE
40# include <sys/modctl.h>
41# include <sys/msio.h>
42# include <sys/stat.h>
43# include <sys/ddi.h>
44# include <sys/strsun.h>
45# include <sys/stropts.h>
46# include <sys/sunddi.h>
47# include <sys/vuid_event.h>
48# include <sys/vuid_wheel.h>
49#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */
50#else /* TESTCASE */
51# undef IN_RING3
52# define IN_RING0
53#endif /* TESTCASE */
54
55#ifdef TESTCASE /* Include this last as we . */
56# include "testcase/solaris.h"
57# include <iprt/test.h>
58#endif /* TESTCASE */
59
60
61/*********************************************************************************************************************************
62* Defined Constants And Macros *
63*********************************************************************************************************************************/
64
65/** The module name. */
66#define DEVICE_NAME "vboxms"
67/** The module description as seen in 'modinfo'. */
68#define DEVICE_DESC "VBoxMouseIntegr"
69
70
71/*********************************************************************************************************************************
72* Internal functions used in global structures *
73*********************************************************************************************************************************/
74
75static int vbmsSolAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd);
76static int vbmsSolDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd);
77static int vbmsSolGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg,
78 void **ppvResult);
79static int vbmsSolOpen(queue_t *pReadQueue, dev_t *pDev, int fFlag,
80 int fMode, cred_t *pCred);
81static int vbmsSolClose(queue_t *pReadQueue, int fFlag, cred_t *pCred);
82static int vbmsSolWPut(queue_t *pWriteQueue, mblk_t *pMBlk);
83
84
85/*********************************************************************************************************************************
86* Driver global structures *
87*********************************************************************************************************************************/
88
89#ifndef TESTCASE /* I see no value in including these in the test. */
90
91/*
92 * mod_info: STREAMS module information.
93 */
94static struct module_info g_vbmsSolModInfo =
95{
96 0, /* module id number */
97 "vboxms",
98 0, /* minimum packet size */
99 INFPSZ, /* maximum packet size accepted */
100 512, /* high water mark for data flow control */
101 128 /* low water mark */
102};
103
104/*
105 * rinit: read queue structure for handling messages coming from below. In
106 * our case this means the host and the virtual hardware, so we do not need
107 * the put and service procedures.
108 */
109static struct qinit g_vbmsSolRInit =
110{
111 NULL, /* put */
112 NULL, /* service thread procedure */
113 vbmsSolOpen,
114 vbmsSolClose,
115 NULL, /* reserved */
116 &g_vbmsSolModInfo,
117 NULL /* module statistics structure */
118};
119
120/*
121 * winit: write queue structure for handling messages coming from above. Above
122 * means user space applications: either Guest Additions user space tools or
123 * applications reading pointer input. Messages from the last most likely pass
124 * through at least the "consms" console mouse streams module which multiplexes
125 * hardware pointer drivers to a single virtual pointer.
126 */
127static struct qinit g_vbmsSolWInit =
128{
129 vbmsSolWPut,
130 NULL, /* service thread procedure */
131 NULL, /* open */
132 NULL, /* close */
133 NULL, /* reserved */
134 &g_vbmsSolModInfo,
135 NULL /* module statistics structure */
136};
137
138/**
139 * streamtab: for drivers that support char/block entry points.
140 */
141static struct streamtab g_vbmsSolStreamTab =
142{
143 &g_vbmsSolRInit,
144 &g_vbmsSolWInit,
145 NULL, /* MUX rinit */
146 NULL /* MUX winit */
147};
148
149/**
150 * cb_ops: for drivers that support char/block entry points
151 */
152static struct cb_ops g_vbmsSolCbOps =
153{
154 nodev, /* open */
155 nodev, /* close */
156 nodev, /* b strategy */
157 nodev, /* b dump */
158 nodev, /* b print */
159 nodev, /* c read */
160 nodev, /* c write */
161 nodev, /* c ioctl */
162 nodev, /* c devmap */
163 nodev, /* c mmap */
164 nodev, /* c segmap */
165 nochpoll, /* c poll */
166 ddi_prop_op, /* property ops */
167 &g_vbmsSolStreamTab,
168 D_MP,
169 CB_REV /* revision */
170};
171
172/**
173 * dev_ops: for driver device operations
174 */
175static struct dev_ops g_vbmsSolDevOps =
176{
177 DEVO_REV, /* driver build revision */
178 0, /* ref count */
179 vbmsSolGetInfo,
180 nulldev, /* identify */
181 nulldev, /* probe */
182 vbmsSolAttach,
183 vbmsSolDetach,
184 nodev, /* reset */
185 &g_vbmsSolCbOps,
186 NULL, /* bus operations */
187 nodev /* power */
188};
189
190/**
191 * modldrv: export driver specifics to the kernel
192 */
193static struct modldrv g_vbmsSolModule =
194{
195 &mod_driverops, /* extern from kernel */
196 DEVICE_DESC " " VBOX_VERSION_STRING "r" RT_XSTR(VBOX_SVN_REV),
197 &g_vbmsSolDevOps
198};
199
200/**
201 * modlinkage: export install/remove/info to the kernel.
202 */
203static struct modlinkage g_vbmsSolModLinkage =
204{
205 MODREV_1, /* loadable module system revision */
206 &g_vbmsSolModule,
207 NULL /* terminate array of linkage structures */
208};
209
210#else /* TESTCASE */
211static void *g_vbmsSolModLinkage;
212#endif /* TESTCASE */
213
214/**
215 * State info for each open file handle.
216 */
217typedef struct
218{
219 /** Device handle. */
220 dev_info_t *pDip;
221 /** Mutex protecting the guest library against multiple initialistation or
222 * uninitialisation. */
223 kmutex_t InitMtx;
224 /** Initialisation counter for the guest library. */
225 size_t cInits;
226 /** The STREAMS write queue which we need for sending messages up to
227 * user-space. */
228 queue_t *pWriteQueue;
229 /** Pre-allocated mouse status VMMDev request for use in the IRQ
230 * handler. */
231 VMMDevReqMouseStatus *pMouseStatusReq;
232 /* The current greatest horizontal pixel offset on the screen, used for
233 * absolute mouse position reporting.
234 */
235 int cMaxScreenX;
236 /* The current greatest vertical pixel offset on the screen, used for
237 * absolute mouse position reporting.
238 */
239 int cMaxScreenY;
240} VBMSSTATE, *PVBMSSTATE;
241
242
243/*********************************************************************************************************************************
244* Global Variables *
245*********************************************************************************************************************************/
246
247/** Global driver state. Actually this could be allocated dynamically. */
248static VBMSSTATE g_OpenNodeState /* = { 0 } */;
249
250
251/*********************************************************************************************************************************
252* Kernel entry points *
253*********************************************************************************************************************************/
254
255/** Driver initialisation. */
256int _init(void)
257{
258 int rc;
259 LogRelFlow((DEVICE_NAME ": built on " __DATE__ " at " __TIME__ "\n"));
260 mutex_init(&g_OpenNodeState.InitMtx, NULL, MUTEX_DRIVER, NULL);
261 /*
262 * Prevent module autounloading.
263 */
264 modctl_t *pModCtl = mod_getctl(&g_vbmsSolModLinkage);
265 if (pModCtl)
266 pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
267 else
268 LogRel((DEVICE_NAME ": failed to disable autounloading!\n"));
269 rc = mod_install(&g_vbmsSolModLinkage);
270
271 LogRelFlow((DEVICE_NAME ": initialisation returning %d.\n", rc));
272 return rc;
273}
274
275
276#ifdef TESTCASE
277/** Simple test of the flow through _init. */
278static void test_init(RTTEST hTest)
279{
280 RTTestSub(hTest, "Testing _init");
281 RTTEST_CHECK(hTest, _init() == 0);
282}
283#endif
284
285
286/** Driver cleanup. */
287int _fini(void)
288{
289 int rc;
290
291 LogRelFlow((DEVICE_NAME ":_fini\n"));
292 rc = mod_remove(&g_vbmsSolModLinkage);
293 if (!rc)
294 mutex_destroy(&g_OpenNodeState.InitMtx);
295
296 return rc;
297}
298
299
300/** Driver identification. */
301int _info(struct modinfo *pModInfo)
302{
303 int rc;
304 LogRelFlow((DEVICE_NAME ":_info\n"));
305 rc = mod_info(&g_vbmsSolModLinkage, pModInfo);
306 LogRelFlow((DEVICE_NAME ":_info returning %d\n", rc));
307 return rc;
308}
309
310
311/*********************************************************************************************************************************
312* Initialisation entry points *
313*********************************************************************************************************************************/
314
315/**
316 * Attach entry point, to attach a device to the system or resume it.
317 *
318 * @param pDip The module structure instance.
319 * @param enmCmd Attach type (ddi_attach_cmd_t)
320 *
321 * @return corresponding solaris error code.
322 */
323int vbmsSolAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
324{
325 LogRelFlow((DEVICE_NAME "::Attach\n"));
326 switch (enmCmd)
327 {
328 case DDI_ATTACH:
329 {
330 int rc;
331 /* Only one instance supported. */
332 if (!ASMAtomicCmpXchgPtr(&g_OpenNodeState.pDip, pDip, NULL))
333 return DDI_FAILURE;
334 rc = ddi_create_minor_node(pDip, DEVICE_NAME, S_IFCHR, 0 /* instance */, DDI_PSEUDO, 0 /* flags */);
335 if (rc == DDI_SUCCESS)
336 return DDI_SUCCESS;
337 ASMAtomicWritePtr(&g_OpenNodeState.pDip, NULL);
338 return DDI_FAILURE;
339 }
340
341 case DDI_RESUME:
342 {
343 /** @todo implement resume for guest driver. */
344 return DDI_SUCCESS;
345 }
346
347 default:
348 return DDI_FAILURE;
349 }
350}
351
352
353/**
354 * Detach entry point, to detach a device to the system or suspend it.
355 *
356 * @param pDip The module structure instance.
357 * @param enmCmd Attach type (ddi_attach_cmd_t)
358 *
359 * @return corresponding solaris error code.
360 */
361int vbmsSolDetach(dev_info_t *pDip, ddi_detach_cmd_t enmCmd)
362{
363 LogRelFlow((DEVICE_NAME "::Detach\n"));
364 switch (enmCmd)
365 {
366 case DDI_DETACH:
367 {
368 ddi_remove_minor_node(pDip, NULL);
369 ASMAtomicWritePtr(&g_OpenNodeState.pDip, NULL);
370 return DDI_SUCCESS;
371 }
372
373 case DDI_SUSPEND:
374 {
375 /** @todo implement suspend for guest driver. */
376 return DDI_SUCCESS;
377 }
378
379 default:
380 return DDI_FAILURE;
381 }
382}
383
384
385/**
386 * Info entry point, called by solaris kernel for obtaining driver info.
387 *
388 * @param pDip The module structure instance (do not use).
389 * @param enmCmd Information request type.
390 * @param pvArg Type specific argument.
391 * @param ppvResult Where to store the requested info.
392 *
393 * @return corresponding solaris error code.
394 */
395int vbmsSolGetInfo(dev_info_t *pDip, ddi_info_cmd_t enmCmd, void *pvArg,
396 void **ppvResult)
397{
398 LogRelFlow((DEVICE_NAME "::GetInfo\n"));
399
400 int rc = DDI_SUCCESS;
401 switch (enmCmd)
402 {
403 case DDI_INFO_DEVT2DEVINFO:
404 {
405 *ppvResult = (void *)g_OpenNodeState.pDip;
406 if (!*ppvResult)
407 rc = DDI_FAILURE;
408 break;
409 }
410
411 case DDI_INFO_DEVT2INSTANCE:
412 {
413 /* There can only be a single-instance of this driver and thus its instance number is 0. */
414 *ppvResult = (void *)0;
415 break;
416 }
417
418 default:
419 rc = DDI_FAILURE;
420 break;
421 }
422
423 NOREF(pvArg);
424 return rc;
425}
426
427
428/*********************************************************************************************************************************
429* Main code *
430*********************************************************************************************************************************/
431
432static void vbmsSolNotify(void *pvState);
433static void vbmsSolVUIDPutAbsEvent(PVBMSSTATE pState, ushort_t cEvent,
434 int cValue);
435
436/**
437 * Open callback for the read queue, which we use as a generic device open
438 * handler.
439 */
440int vbmsSolOpen(queue_t *pReadQueue, dev_t *pDev, int fFlag, int fMode,
441 cred_t *pCred)
442{
443 PVBMSSTATE pState = NULL;
444 int rc = VINF_SUCCESS;
445
446 NOREF(fFlag);
447 NOREF(pCred);
448 LogRelFlow((DEVICE_NAME "::Open, pWriteQueue=%p\n", WR(pReadQueue)));
449
450 /*
451 * Sanity check on the mode parameter - only open as a driver, not a
452 * module, and we do cloning ourselves.
453 */
454 if (fMode)
455 {
456 LogRel(("::Open: invalid attempt to clone device."));
457 return EINVAL;
458 }
459
460 pState = &g_OpenNodeState;
461 mutex_enter(&pState->InitMtx);
462 /*
463 * Check and remember our STREAM queue.
464 */
465 if ( pState->pWriteQueue
466 && (pState->pWriteQueue != WR(pReadQueue)))
467 {
468 mutex_exit(&pState->InitMtx);
469 LogRel((DEVICE_NAME "::Open: unexpectedly called with a different queue to previous calls. Exiting.\n"));
470 return EINVAL;
471 }
472 if (!pState->cInits)
473 {
474 /*
475 * Initialize IPRT R0 driver, which internally calls OS-specific r0
476 * init, and create a new session.
477 */
478 rc = VbglR0InitClient();
479 if (RT_SUCCESS(rc))
480 {
481 rc = VbglR0GRAlloc((VMMDevRequestHeader **)
482 &pState->pMouseStatusReq,
483 sizeof(*pState->pMouseStatusReq),
484 VMMDevReq_GetMouseStatus);
485 if (RT_FAILURE(rc))
486 VbglR0TerminateClient();
487 else
488 {
489 int rc2;
490 /* Initialise user data for the queues to our state and
491 * vice-versa. */
492 pState->pWriteQueue = WR(pReadQueue);
493 WR(pReadQueue)->q_ptr = (char *)pState;
494 pReadQueue->q_ptr = (char *)pState;
495 qprocson(pReadQueue);
496 /* Enable our IRQ handler. */
497 rc2 = VbglR0SetMouseNotifyCallback(vbmsSolNotify, (void *)pState);
498 if (RT_FAILURE(rc2))
499 /* Log the failure. I may well have not understood what
500 * is going on here, and the logging may help me. */
501 LogRelFlow(("Failed to install the event handler call-back, rc=%Rrc\n",
502 rc2));
503 }
504 }
505 }
506 if (RT_SUCCESS(rc))
507 ++pState->cInits;
508 mutex_exit(&pState->InitMtx);
509 if (RT_FAILURE(rc))
510 {
511 LogRel(("open time initialisation failed. rc=%d\n", rc));
512 ASMAtomicWriteNullPtr(&pState->pWriteQueue);
513 return EINVAL;
514 }
515 return 0;
516}
517
518
519/**
520 * Notification callback, called when the VBoxGuest mouse pointer is moved.
521 * We send a VUID event up to user space. We may send a miscalculated event
522 * if a resolution change is half-way through, but that is pretty much to be
523 * expected, so we won't worry about it.
524 */
525void vbmsSolNotify(void *pvState)
526{
527 PVBMSSTATE pState = (PVBMSSTATE)pvState;
528 int rc;
529
530 pState->pMouseStatusReq->mouseFeatures = 0;
531 pState->pMouseStatusReq->pointerXPos = 0;
532 pState->pMouseStatusReq->pointerYPos = 0;
533 rc = VbglR0GRPerform(&pState->pMouseStatusReq->header);
534 if (RT_SUCCESS(rc))
535 {
536 int cMaxScreenX = pState->cMaxScreenX;
537 int cMaxScreenY = pState->cMaxScreenY;
538 int x = pState->pMouseStatusReq->pointerXPos;
539 int y = pState->pMouseStatusReq->pointerYPos;
540
541 if (cMaxScreenX && cMaxScreenY)
542 {
543 vbmsSolVUIDPutAbsEvent(pState, LOC_X_ABSOLUTE,
544 x * cMaxScreenX / VMMDEV_MOUSE_RANGE_MAX);
545 vbmsSolVUIDPutAbsEvent(pState, LOC_Y_ABSOLUTE,
546 y * cMaxScreenY / VMMDEV_MOUSE_RANGE_MAX);
547 }
548 }
549}
550
551
552void vbmsSolVUIDPutAbsEvent(PVBMSSTATE pState, ushort_t cEvent,
553 int cValue)
554{
555 queue_t *pReadQueue = RD(pState->pWriteQueue);
556 mblk_t *pMBlk = allocb(sizeof(Firm_event), BPRI_HI);
557 Firm_event *pEvent;
558 AssertReturnVoid(cEvent == LOC_X_ABSOLUTE || cEvent == LOC_Y_ABSOLUTE);
559 if (!pMBlk)
560 return; /* If kernel memory is short a missed event is acceptable! */
561 pEvent = (Firm_event *)pMBlk->b_wptr;
562 pEvent->id = cEvent;
563 pEvent->pair_type = FE_PAIR_DELTA;
564 pEvent->pair = cEvent == LOC_X_ABSOLUTE ? LOC_X_DELTA : LOC_Y_DELTA;
565 pEvent->value = cValue;
566 uniqtime32(&pEvent->time);
567 pMBlk->b_wptr += sizeof(Firm_event);
568 /* Put the message on the queue immediately if it is not blocked. */
569 if (canput(pReadQueue->q_next))
570 putnext(pReadQueue, pMBlk);
571 else
572 putq(pReadQueue, pMBlk);
573}
574
575
576/**
577 * Close callback for the read queue, which we use as a generic device close
578 * handler.
579 */
580int vbmsSolClose(queue_t *pReadQueue, int fFlag, cred_t *pCred)
581{
582 PVBMSSTATE pState = (PVBMSSTATE)pReadQueue->q_ptr;
583
584 LogRelFlow((DEVICE_NAME "::Close, pWriteQueue=%p\n", WR(pReadQueue)));
585 NOREF(fFlag);
586 NOREF(pCred);
587
588 if (!pState)
589 {
590 Log((DEVICE_NAME "::Close: failed to get pState.\n"));
591 return EFAULT;
592 }
593
594 mutex_enter(&pState->InitMtx);
595 --pState->cInits;
596 if (!pState->cInits)
597 {
598 VbglR0SetMouseStatus(0);
599 /* Disable our IRQ handler. */
600 VbglR0SetMouseNotifyCallback(NULL, NULL);
601 qprocsoff(pReadQueue);
602
603 /*
604 * Close the session.
605 */
606 ASMAtomicWriteNullPtr(&pState->pWriteQueue);
607 pReadQueue->q_ptr = NULL;
608 VbglR0GRFree(&pState->pMouseStatusReq->header);
609 VbglR0TerminateClient();
610 }
611 mutex_exit(&pState->InitMtx);
612 return 0;
613}
614
615
616#ifdef TESTCASE
617/** Simple test of vbmsSolOpen and vbmsSolClose. */
618static void testOpenClose(RTTEST hTest)
619{
620 queue_t aQueues[2];
621 dev_t device = 0;
622 int rc;
623
624 RTTestSub(hTest, "Testing vbmsSolOpen and vbmsSolClose");
625 RT_ZERO(g_OpenNodeState);
626 RT_ZERO(aQueues);
627 doInitQueues(&aQueues[0]);
628 rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
629 RTTEST_CHECK(hTest, rc == 0);
630 RTTEST_CHECK(hTest, g_OpenNodeState.pWriteQueue == WR(&aQueues[0]));
631 vbmsSolClose(RD(&aQueues[0]), 0, NULL);
632}
633#endif
634
635
636/* Helper for vbmsSolWPut. */
637static int vbmsSolDispatchIOCtl(PVBMSSTATE pState, mblk_t *pMBlk);
638
639/**
640 * Handler for messages sent from above (user-space and upper modules) which
641 * land in our write queue.
642 */
643int vbmsSolWPut(queue_t *pWriteQueue, mblk_t *pMBlk)
644{
645 PVBMSSTATE pState = (PVBMSSTATE)pWriteQueue->q_ptr;
646 LogRelFlowFunc((DEVICE_NAME "::"));
647 switch (pMBlk->b_datap->db_type)
648 {
649 case M_FLUSH:
650 LogRelFlow(("M_FLUSH, FLUSHW=%RTbool, FLUSHR=%RTbool\n",
651 *pMBlk->b_rptr & FLUSHW, *pMBlk->b_rptr & FLUSHR));
652 /* Flush the write queue if so requested. */
653 if (*pMBlk->b_rptr & FLUSHW)
654 flushq(pWriteQueue, FLUSHDATA);
655
656 /* Flush the read queue if so requested. */
657 if (*pMBlk->b_rptr & FLUSHR)
658 flushq(RD(pWriteQueue), FLUSHDATA);
659
660 /* We have no one below us to pass the message on to. */
661 freemsg(pMBlk);
662 return 0;
663 /* M_IOCDATA is additional data attached to (at least) transparent
664 * IOCtls. We handle the two together here and separate them further
665 * down. */
666 case M_IOCTL:
667 case M_IOCDATA:
668 {
669 int err;
670
671 LogRelFlow(( pMBlk->b_datap->db_type == M_IOCTL
672 ? "M_IOCTL\n" : "M_IOCDATA\n"));
673 err = vbmsSolDispatchIOCtl(pState, pMBlk);
674 if (!err)
675 qreply(pWriteQueue, pMBlk);
676 else
677 miocnak(pWriteQueue, pMBlk, 0, err);
678 break;
679 }
680 default:
681 LogRelFlow(("Unknown command, not acknowledging.\n"));
682 }
683 return 0;
684}
685
686
687#ifdef TESTCASE
688/* Constants, definitions and test functions for testWPut. */
689static const int g_ccTestWPutFirmEvent = VUID_FIRM_EVENT;
690# define PVGFORMAT (&g_ccTestWPutFirmEvent)
691# define CBGFORMAT (sizeof(g_ccTestWPutFirmEvent))
692static const Ms_screen_resolution g_TestResolution = { 640, 480 };
693# define PMSIOSRES (&g_TestResolution)
694# define CBMSIOSRES (sizeof(g_TestResolution))
695
696static inline bool testSetResolution(RTTEST hTest, queue_t *pWriteQueue,
697 struct msgb *pMBlk)
698{
699 PVBMSSTATE pState = (PVBMSSTATE)pWriteQueue->q_ptr;
700 RTTEST_CHECK_MSG_RET(hTest, pState->cMaxScreenX
701 == g_TestResolution.width - 1,
702 (hTest, "pState->cMaxScreenX=%d\n",
703 pState->cMaxScreenX), false);
704 RTTEST_CHECK_MSG_RET(hTest, pState->cMaxScreenY
705 == g_TestResolution.height - 1,
706 (hTest, "pState->cMaxScreenY=%d\n",
707 pState->cMaxScreenY), false);
708 return true;
709}
710
711/** Data table for testWPut. */
712static struct
713{
714 int iIOCCmd;
715 size_t cbData;
716 const void *pvDataIn;
717 size_t cbDataIn;
718 const void *pvDataOut;
719 size_t cbDataOut;
720 int rcExp;
721 bool (*pfnExtra)(RTTEST hTest, queue_t *pWriteQueue, struct msgb *pMBlk);
722 bool fCanTransparent;
723} g_asTestWPut[] =
724{
725 /* iIOCCmd cbData pvDataIn cbDataIn
726 pvDataOut cbDataOut rcExp pfnExtra fCanTransparent */
727 { VUIDGFORMAT, sizeof(int), NULL, 0,
728 PVGFORMAT, CBGFORMAT, 0, NULL, true },
729 { VUIDGFORMAT, sizeof(int) - 1, NULL, 0,
730 NULL, 0, EINVAL, NULL, false },
731 { VUIDGFORMAT, sizeof(int) + 1, NULL, 0,
732 PVGFORMAT, CBGFORMAT, 0, NULL, true },
733 { VUIDSFORMAT, sizeof(int), PVGFORMAT, CBGFORMAT,
734 NULL, 0, 0, NULL, true },
735 { MSIOSRESOLUTION, CBMSIOSRES, PMSIOSRES, CBMSIOSRES,
736 NULL, 0, 0, testSetResolution, true },
737 { VUIDGWHEELINFO, 0, NULL, 0,
738 NULL, 0, EINVAL, NULL, true }
739};
740
741# undef PVGFORMAT
742# undef CBGFORMAT
743# undef PMSIOSRES
744# undef CBMSIOSRES
745
746/* Helpers for testWPut. */
747static void testWPutStreams(RTTEST hTest, unsigned i);
748static void testWPutTransparent(RTTEST hTest, unsigned i);
749static void testWPutIOCDataIn(RTTEST hTest, unsigned i);
750static void testWPutIOCDataOut(RTTEST hTest, unsigned i);
751
752/** Test WPut's handling of different IOCtls, which is bulk of the logic in
753 * this file. */
754static void testWPut(RTTEST hTest)
755{
756 unsigned i;
757
758 RTTestSub(hTest, "Testing vbmsWPut");
759 for (i = 0; i < RT_ELEMENTS(g_asTestWPut); ++i)
760 {
761 AssertReturnVoid(g_asTestWPut[i].cbDataIn <= g_asTestWPut[i].cbData);
762 AssertReturnVoid(g_asTestWPut[i].cbDataOut <= g_asTestWPut[i].cbData);
763 testWPutStreams(hTest, i);
764 if (g_asTestWPut[i].fCanTransparent)
765 testWPutTransparent(hTest, i);
766 if (g_asTestWPut[i].fCanTransparent && g_asTestWPut[i].cbDataIn)
767 testWPutIOCDataIn(hTest, i);
768 if (g_asTestWPut[i].fCanTransparent && g_asTestWPut[i].cbDataOut)
769 testWPutIOCDataOut(hTest, i);
770 }
771}
772
773
774#define MSG_DATA_SIZE 1024
775
776/** Simulate sending a streams IOCtl to WPut with the parameters from table
777 * line @a i. */
778void testWPutStreams(RTTEST hTest, unsigned i)
779{
780 queue_t aQueues[2];
781 dev_t device = 0;
782 struct msgb *pMBlk = allocb(sizeof(struct iocblk), BPRI_MED);
783 struct msgb *pMBlkCont = allocb(MSG_DATA_SIZE, BPRI_MED);
784 struct iocblk *pIOCBlk = pMBlk ? (struct iocblk *)pMBlk->b_rptr : NULL;
785 int rc, cFormat = 0;
786
787 AssertReturnVoid(pMBlk);
788 AssertReturnVoidStmt(pMBlkCont, freemsg(pMBlk));
789 RT_ZERO(aQueues);
790 doInitQueues(&aQueues[0]);
791 rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
792 RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
793 RTTEST_CHECK_MSG(hTest, g_OpenNodeState.pWriteQueue
794 == WR(&aQueues[0]), (hTest, "i=%u\n", i));
795 pMBlk->b_datap->db_type = M_IOCTL;
796 pIOCBlk->ioc_cmd = g_asTestWPut[i].iIOCCmd;
797 pIOCBlk->ioc_count = g_asTestWPut[i].cbData;
798 AssertReturnVoid(g_asTestWPut[i].cbData <= MSG_DATA_SIZE);
799 memcpy(pMBlkCont->b_rptr, g_asTestWPut[i].pvDataIn,
800 g_asTestWPut[i].cbDataIn);
801 pMBlk->b_cont = pMBlkCont;
802 rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
803 RTTEST_CHECK_MSG(hTest, pIOCBlk->ioc_error == g_asTestWPut[i].rcExp,
804 (hTest, "i=%u, IOCBlk.ioc_error=%d\n", i,
805 pIOCBlk->ioc_error));
806 RTTEST_CHECK_MSG(hTest, pIOCBlk->ioc_count == g_asTestWPut[i].cbDataOut,
807 (hTest, "i=%u, ioc_count=%u\n", i, pIOCBlk->ioc_count));
808 RTTEST_CHECK_MSG(hTest, !memcmp(pMBlkCont->b_rptr,
809 g_asTestWPut[i].pvDataOut,
810 g_asTestWPut[i].cbDataOut),
811 (hTest, "i=%u\n", i));
812 /* Hack to ensure that miocpullup() gets called when needed. */
813 if (g_asTestWPut[i].cbData > 0)
814 RTTEST_CHECK_MSG(hTest, pMBlk->b_flag == 1, (hTest, "i=%u\n", i));
815 if (!g_asTestWPut[i].rcExp)
816 RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
817 (hTest, "i=%u\n", i));
818 if (g_asTestWPut[i].pfnExtra)
819 if (!g_asTestWPut[i].pfnExtra(hTest, WR(&aQueues[0]), pMBlk))
820 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Called from %s.\n",
821 __PRETTY_FUNCTION__);
822 vbmsSolClose(RD(&aQueues[1]), 0, NULL);
823 freemsg(pMBlk);
824}
825
826
827#define USER_ADDRESS 0xfeedbacc
828
829/** Simulate sending a transparent IOCtl to WPut with the parameters from table
830 * line @a i. */
831void testWPutTransparent(RTTEST hTest, unsigned i)
832{
833 queue_t aQueues[2];
834 dev_t device = 0;
835 struct msgb *pMBlk = allocb(sizeof(struct iocblk), BPRI_MED);
836 struct msgb *pMBlkCont = allocb(sizeof(void *), BPRI_MED);
837 struct iocblk *pIOCBlk = pMBlk ? (struct iocblk *)pMBlk->b_rptr : NULL;
838 struct copyreq *pCopyReq;
839 int rc, cFormat = 0;
840
841 /* if (g_asTestWPut[i].cbDataIn == 0 && g_asTestWPut[i].cbDataOut != 0)
842 return; */ /* This case will be handled once the current ones work. */
843 AssertReturnVoid(pMBlk);
844 AssertReturnVoidStmt(pMBlkCont, freemsg(pMBlk));
845 RT_ZERO(aQueues);
846 doInitQueues(&aQueues[0]);
847 rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
848 RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
849 RTTEST_CHECK_MSG(hTest, g_OpenNodeState.pWriteQueue
850 == WR(&aQueues[0]), (hTest, "i=%u\n", i));
851 pMBlk->b_datap->db_type = M_IOCTL;
852 pIOCBlk->ioc_cmd = g_asTestWPut[i].iIOCCmd;
853 pIOCBlk->ioc_count = TRANSPARENT;
854 *(void **)pMBlkCont->b_rptr = (void *)USER_ADDRESS;
855 pMBlk->b_cont = pMBlkCont;
856 rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
857 pCopyReq = (struct copyreq *)pMBlk->b_rptr;
858 RTTEST_CHECK_MSG(hTest, ( ( g_asTestWPut[i].cbDataIn
859 && (pMBlk->b_datap->db_type == M_COPYIN))
860 || ( g_asTestWPut[i].cbDataOut
861 && (pMBlk->b_datap->db_type == M_COPYOUT))
862 || ( (g_asTestWPut[i].rcExp == 0)
863 && pMBlk->b_datap->db_type == M_IOCACK)
864 || (pMBlk->b_datap->db_type == M_IOCNAK)),
865 (hTest, "i=%u, db_type=%u\n", i,
866 (unsigned) pMBlk->b_datap->db_type));
867 /* Our TRANSPARENT IOCtls can only return non-zero if they have no payload.
868 * Others should either return zero or be non-TRANSPARENT only. */
869 if (pMBlk->b_datap->db_type == M_IOCNAK)
870 RTTEST_CHECK_MSG(hTest, pIOCBlk->ioc_error == g_asTestWPut[i].rcExp,
871 (hTest, "i=%u, IOCBlk.ioc_error=%d\n", i,
872 pIOCBlk->ioc_error));
873 if (g_asTestWPut[i].cbData)
874 {
875 RTTEST_CHECK_MSG(hTest, pCopyReq->cq_addr == (char *)USER_ADDRESS,
876 (hTest, "i=%u, cq_addr=%p\n", i, pCopyReq->cq_addr));
877 RTTEST_CHECK_MSG( hTest, pCopyReq->cq_size
878 == g_asTestWPut[i].cbDataIn
879 ? g_asTestWPut[i].cbDataIn
880 : g_asTestWPut[i].cbDataOut,
881 (hTest, "i=%u, cq_size=%llu\n", i,
882 (unsigned long long)pCopyReq->cq_size));
883 }
884 /* Implementation detail - check that the private pointer is correctly
885 * set to the user address *for two direction IOCtls* or NULL otherwise. */
886 if (g_asTestWPut[i].cbDataIn && g_asTestWPut[i].cbDataOut)
887 RTTEST_CHECK_MSG(hTest, pCopyReq->cq_private == (mblk_t *)USER_ADDRESS,
888 (hTest, "i=%u, cq_private=%p\n", i,
889 pCopyReq->cq_private));
890 else if ( (pMBlk->b_datap->db_type == M_COPYIN)
891 || (pMBlk->b_datap->db_type == M_COPYOUT))
892 RTTEST_CHECK_MSG(hTest, !pCopyReq->cq_private,
893 (hTest, "i=%u, cq_private=%p\n", i,
894 pCopyReq->cq_private));
895 if (!g_asTestWPut[i].rcExp)
896 RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
897 (hTest, "i=%u\n", i));
898 if (g_asTestWPut[i].pfnExtra && !g_asTestWPut[i].cbData)
899 if (!g_asTestWPut[i].pfnExtra(hTest, WR(&aQueues[0]), pMBlk))
900 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Called from %s.\n",
901 __PRETTY_FUNCTION__);
902 vbmsSolClose(RD(&aQueues[1]), 0, NULL);
903 freemsg(pMBlk);
904}
905
906
907/** Simulate sending follow-on IOCData messages to a transparent IOCtl to WPut
908 * with the parameters from table line @a i. */
909void testWPutIOCDataIn(RTTEST hTest, unsigned i)
910{
911 queue_t aQueues[2];
912 dev_t device = 0;
913 struct msgb *pMBlk = allocb(sizeof(struct copyresp), BPRI_MED);
914 struct msgb *pMBlkCont = allocb(MSG_DATA_SIZE, BPRI_MED);
915 struct copyresp *pCopyResp = pMBlk ? (struct copyresp *)pMBlk->b_rptr
916 : NULL;
917 void *pvData = pMBlkCont ? pMBlkCont->b_rptr : NULL;
918 struct copyreq *pCopyReq;
919 int rc, cFormat = 0;
920
921 AssertReturnVoid(pMBlk);
922 AssertReturnVoidStmt(pMBlkCont, freemsg(pMBlk));
923 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "%s: i=%u\n", __PRETTY_FUNCTION__,
924 i);
925 AssertReturnVoidStmt(g_asTestWPut[i].cbDataIn, freemsg(pMBlk));
926 RT_ZERO(aQueues);
927 doInitQueues(&aQueues[0]);
928 rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
929 RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
930 RTTEST_CHECK_MSG(hTest, g_OpenNodeState.pWriteQueue
931 == WR(&aQueues[0]), (hTest, "i=%u\n", i));
932 pMBlk->b_datap->db_type = M_IOCDATA;
933 pCopyResp->cp_cmd = g_asTestWPut[i].iIOCCmd;
934 if (g_asTestWPut[i].cbDataOut)
935 pCopyResp->cp_private = USER_ADDRESS;
936 AssertReturnVoid(g_asTestWPut[i].cbData <= MSG_DATA_SIZE);
937 memcpy(pMBlkCont->b_rptr, g_asTestWPut[i].pvDataIn, g_asTestWPut[i].cbDataIn);
938 pMBlk->b_cont = pMBlkCont;
939 rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
940 pCopyReq = (struct copyreq *)pMBlk->b_rptr;
941 RTTEST_CHECK_MSG(hTest, ( ( g_asTestWPut[i].cbDataOut
942 && (pMBlk->b_datap->db_type == M_COPYOUT))
943 || ( (g_asTestWPut[i].rcExp == 0)
944 && pMBlk->b_datap->db_type == M_IOCACK)
945 || (pMBlk->b_datap->db_type == M_IOCNAK)),
946 (hTest, "i=%u, db_type=%u\n", i,
947 (unsigned) pMBlk->b_datap->db_type));
948 if (g_asTestWPut[i].cbDataOut)
949 {
950 RTTEST_CHECK_MSG(hTest, pCopyReq->cq_addr == (char *)pvData,
951 (hTest, "i=%u, cq_addr=%p\n", i, pCopyReq->cq_addr));
952 RTTEST_CHECK_MSG(hTest, pCopyReq->cq_size == g_asTestWPut[i].cbData,
953 (hTest, "i=%u, cq_size=%llu\n", i,
954 (unsigned long long)pCopyReq->cq_size));
955 RTTEST_CHECK_MSG(hTest, !memcmp(pvData, g_asTestWPut[i].pvDataOut,
956 g_asTestWPut[i].cbDataOut),
957 (hTest, "i=%u\n", i));
958 }
959 RTTEST_CHECK_MSG(hTest, !pCopyReq->cq_private,
960 (hTest, "i=%u, cq_private=%p\n", i,
961 pCopyReq->cq_private));
962 if (!g_asTestWPut[i].rcExp)
963 RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
964 (hTest, "i=%u\n", i));
965 if (g_asTestWPut[i].pfnExtra && !g_asTestWPut[i].cbDataOut)
966 if (!g_asTestWPut[i].pfnExtra(hTest, WR(&aQueues[0]), pMBlk))
967 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Called from %s.\n",
968 __PRETTY_FUNCTION__);
969 vbmsSolClose(RD(&aQueues[1]), 0, NULL);
970 freemsg(pMBlk);
971}
972
973
974/** Simulate sending follow-on IOCData messages to a transparent IOCtl to WPut
975 * with the parameters from table line @a i. */
976void testWPutIOCDataOut(RTTEST hTest, unsigned i)
977{
978 queue_t aQueues[2];
979 dev_t device = 0;
980 struct msgb *pMBlk = allocb(sizeof(struct copyresp), BPRI_MED);
981 struct copyresp *pCopyResp = pMBlk ? (struct copyresp *)pMBlk->b_rptr
982 : NULL;
983 int rc, cFormat = 0;
984
985 AssertReturnVoid(pMBlk);
986 AssertReturnVoidStmt(g_asTestWPut[i].cbDataOut, freemsg(pMBlk));
987 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "%s: i=%u\n", __PRETTY_FUNCTION__,
988 i);
989 RT_ZERO(aQueues);
990 doInitQueues(&aQueues[0]);
991 rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
992 RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
993 RTTEST_CHECK_MSG(hTest, g_OpenNodeState.pWriteQueue
994 == WR(&aQueues[0]), (hTest, "i=%u\n", i));
995 pMBlk->b_datap->db_type = M_IOCDATA;
996 pCopyResp->cp_cmd = g_asTestWPut[i].iIOCCmd;
997 rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
998 RTTEST_CHECK_MSG(hTest, pMBlk->b_datap->db_type == M_IOCACK,
999 (hTest, "i=%u, db_type=%u\n", i,
1000 (unsigned) pMBlk->b_datap->db_type));
1001 if (!g_asTestWPut[i].rcExp)
1002 RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
1003 (hTest, "i=%u\n", i));
1004 vbmsSolClose(RD(&aQueues[1]), 0, NULL);
1005 freemsg(pMBlk);
1006}
1007#endif
1008
1009
1010/** Data transfer direction of an IOCtl. This is used for describing
1011 * transparent IOCtls, and @a UNSPECIFIED is not a valid value for them. */
1012enum IOCTLDIRECTION
1013{
1014 /** This IOCtl transfers no data. */
1015 NONE,
1016 /** This IOCtl only transfers data from user to kernel. */
1017 IN,
1018 /** This IOCtl only transfers data from kernel to user. */
1019 OUT,
1020 /** This IOCtl transfers data from user to kernel and back. */
1021 BOTH,
1022 /** We aren't saying anything about how the IOCtl transfers data. */
1023 UNSPECIFIED
1024};
1025
1026/**
1027 * IOCtl handler function.
1028 * @returns 0 on success, error code on failure.
1029 * @param iCmd The IOCtl command number.
1030 * @param pvData Buffer for the user data.
1031 * @param cbBuffer Size of the buffer in @a pvData or zero.
1032 * @param pcbData Where to set the size of the data returned. Required for
1033 * handlers which return data.
1034 * @param prc Where to store the return code. Default is zero. Only
1035 * used for IOCtls without data for convenience of
1036 * implemention.
1037 */
1038typedef int FNVBMSSOLIOCTL(PVBMSSTATE pState, int iCmd, void *pvData,
1039 size_t cbBuffer, size_t *pcbData, int *prc);
1040typedef FNVBMSSOLIOCTL *PFNVBMSSOLIOCTL;
1041
1042/* Helpers for vbmsSolDispatchIOCtl. */
1043static int vbmsSolHandleIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
1044 PFNVBMSSOLIOCTL pfnHandler,
1045 int iCmd, size_t cbCmd,
1046 enum IOCTLDIRECTION enmDirection);
1047static int vbmsSolVUIDIOCtl(PVBMSSTATE pState, int iCmd, void *pvData,
1048 size_t cbBuffer, size_t *pcbData, int *prc);
1049
1050/** Table of supported VUID IOCtls. */
1051struct
1052{
1053 /** The IOCtl number. */
1054 int iCmd;
1055 /** The size of the buffer which needs to be copied between user and kernel
1056 * space, or zero if unknown (must be known for tranparent IOCtls). */
1057 size_t cbBuffer;
1058 /** The direction the buffer data needs to be copied. This must be
1059 * specified for transparent IOCtls. */
1060 enum IOCTLDIRECTION enmDirection;
1061} g_aVUIDIOCtlDescriptions[] =
1062{
1063 { VUIDGFORMAT, sizeof(int), OUT },
1064 { VUIDSFORMAT, sizeof(int), IN },
1065 { VUIDGADDR, 0, UNSPECIFIED },
1066 { VUIDGADDR, 0, UNSPECIFIED },
1067 { MSIOGETPARMS, sizeof(Ms_parms), OUT },
1068 { MSIOSETPARMS, sizeof(Ms_parms), IN },
1069 { MSIOSRESOLUTION, sizeof(Ms_screen_resolution), IN },
1070 { MSIOBUTTONS, sizeof(int), OUT },
1071 { VUIDGWHEELCOUNT, sizeof(int), OUT },
1072 { VUIDGWHEELINFO, 0, UNSPECIFIED },
1073 { VUIDGWHEELSTATE, 0, UNSPECIFIED },
1074 { VUIDSWHEELSTATE, 0, UNSPECIFIED }
1075};
1076
1077/**
1078 * Handle a STREAMS IOCtl message for our driver on the write stream. This
1079 * function takes care of the IOCtl logic only and does not call qreply() or
1080 * miocnak() at all - the caller must call these on success or failure
1081 * respectively.
1082 * @returns 0 on success or the IOCtl error code on failure.
1083 * @param pState pointer to the state structure.
1084 * @param pMBlk pointer to the STREAMS message block structure.
1085 */
1086static int vbmsSolDispatchIOCtl(PVBMSSTATE pState, mblk_t *pMBlk)
1087{
1088 struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
1089 int iCmd = pIOCBlk->ioc_cmd, iCmdType = iCmd & (0xff << 8);
1090 size_t cbBuffer;
1091 enum IOCTLDIRECTION enmDirection;
1092
1093 LogRelFlowFunc((DEVICE_NAME "::pIOCBlk=%p, iCmdType=%c, iCmd=0x%x\n",
1094 pIOCBlk, (char) (iCmdType >> 8), (unsigned)iCmd));
1095 switch (iCmdType)
1096 {
1097 case MSIOC:
1098 case VUIOC:
1099 {
1100 unsigned i;
1101
1102 for (i = 0; i < RT_ELEMENTS(g_aVUIDIOCtlDescriptions); ++i)
1103 if (g_aVUIDIOCtlDescriptions[i].iCmd == iCmd)
1104 {
1105 cbBuffer = g_aVUIDIOCtlDescriptions[i].cbBuffer;
1106 enmDirection = g_aVUIDIOCtlDescriptions[i].enmDirection;
1107 return vbmsSolHandleIOCtl(pState, pMBlk,
1108 vbmsSolVUIDIOCtl, iCmd,
1109 cbBuffer, enmDirection);
1110 }
1111 return EINVAL;
1112 }
1113 default:
1114 return ENOTTY;
1115 }
1116}
1117
1118
1119/* Helpers for vbmsSolHandleIOCtl. */
1120static int vbmsSolHandleIOCtlData(PVBMSSTATE pState, mblk_t *pMBlk,
1121 PFNVBMSSOLIOCTL pfnHandler, int iCmd,
1122 size_t cbCmd,
1123 enum IOCTLDIRECTION enmDirection);
1124
1125static int vbmsSolHandleTransparentIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
1126 PFNVBMSSOLIOCTL pfnHandler,
1127 int iCmd, size_t cbCmd,
1128 enum IOCTLDIRECTION enmDirection);
1129
1130static int vbmsSolHandleIStrIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
1131 PFNVBMSSOLIOCTL pfnHandler, int iCmd);
1132
1133static void vbmsSolAcknowledgeIOCtl(mblk_t *pMBlk, int cbData, int rc)
1134{
1135 struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
1136
1137 pMBlk->b_datap->db_type = M_IOCACK;
1138 pIOCBlk->ioc_count = cbData;
1139 pIOCBlk->ioc_rval = rc;
1140 pIOCBlk->ioc_error = 0;
1141}
1142
1143/**
1144 * Generic code for handling STREAMS-specific IOCtl logic and boilerplate. It
1145 * calls the IOCtl handler passed to it without the handler having to be aware
1146 * of STREAMS structures, or whether this is a transparent (traditional) or an
1147 * I_STR (using a STREAMS structure to describe the data) IOCtl. With the
1148 * caveat that we only support transparent IOCtls which pass all data in a
1149 * single buffer of a fixed size (I_STR IOCtls are restricted to a single
1150 * buffer anyway, but the caller can choose the buffer size).
1151 * @returns 0 on success or the IOCtl error code on failure.
1152 * @param pState pointer to the state structure.
1153 * @param pMBlk pointer to the STREAMS message block structure.
1154 * @param pfnHandler pointer to the right IOCtl handler function for this
1155 * IOCtl number.
1156 * @param iCmd IOCtl command number.
1157 * @param cbCmd size of the user space buffer for this IOCtl number,
1158 * used for processing transparent IOCtls. Pass zero
1159 * for IOCtls with no maximum buffer size (which will
1160 * not be able to be handled as transparent) or with
1161 * no argument.
1162 * @param enmDirection data transfer direction of the IOCtl.
1163 */
1164static int vbmsSolHandleIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
1165 PFNVBMSSOLIOCTL pfnHandler, int iCmd,
1166 size_t cbCmd, enum IOCTLDIRECTION enmDirection)
1167{
1168 struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
1169
1170 LogFlowFunc(("iCmd=0x%x, cbBuffer=%d, enmDirection=%d\n",
1171 (unsigned)iCmd, (int)cbCmd, (int)enmDirection));
1172 if (pMBlk->b_datap->db_type == M_IOCDATA)
1173 return vbmsSolHandleIOCtlData(pState, pMBlk, pfnHandler, iCmd,
1174 cbCmd, enmDirection);
1175 else if ( pMBlk->b_datap->db_type == M_IOCTL
1176 && pIOCBlk->ioc_count == TRANSPARENT)
1177 return vbmsSolHandleTransparentIOCtl(pState, pMBlk, pfnHandler,
1178 iCmd, cbCmd, enmDirection);
1179 else if (pMBlk->b_datap->db_type == M_IOCTL)
1180 return vbmsSolHandleIStrIOCtl(pState, pMBlk, pfnHandler, iCmd);
1181 return EINVAL;
1182}
1183
1184
1185/**
1186 * Helper for vbmsSolHandleIOCtl. This rather complicated-looking
1187 * code is basically the standard boilerplate for handling any streams IOCtl
1188 * additional data, which we currently only use for transparent IOCtls.
1189 * @copydoc vbmsSolHandleIOCtl
1190 */
1191static int vbmsSolHandleIOCtlData(PVBMSSTATE pState, mblk_t *pMBlk,
1192 PFNVBMSSOLIOCTL pfnHandler, int iCmd,
1193 size_t cbCmd,
1194 enum IOCTLDIRECTION enmDirection)
1195{
1196 struct copyresp *pCopyResp = (struct copyresp *)pMBlk->b_rptr;
1197
1198 LogFlowFunc(("iCmd=0x%x, cbBuffer=%d, enmDirection=%d, cp_rval=%d, cp_private=%p\n",
1199 (unsigned)iCmd, (int)cbCmd, (int)enmDirection,
1200 (int)(uintptr_t)pCopyResp->cp_rval,
1201 (void *)pCopyResp->cp_private));
1202 if (pCopyResp->cp_rval) /* cp_rval is a pointer used as a boolean. */
1203 return EAGAIN;
1204 if ((pCopyResp->cp_private && enmDirection == BOTH) || enmDirection == IN)
1205 {
1206 size_t cbData = 0;
1207 void *pvData = NULL;
1208 int err;
1209
1210 if (!pMBlk->b_cont)
1211 return EINVAL;
1212 pvData = pMBlk->b_cont->b_rptr;
1213 err = pfnHandler(pState, iCmd, pvData, cbCmd, &cbData, NULL);
1214 if (!err && enmDirection == BOTH)
1215 mcopyout(pMBlk, NULL, cbData, pCopyResp->cp_private, NULL);
1216 else if (!err && enmDirection == IN)
1217 vbmsSolAcknowledgeIOCtl(pMBlk, 0, 0);
1218 if ((err || enmDirection == IN) && pCopyResp->cp_private)
1219 freemsg(pCopyResp->cp_private);
1220 return err;
1221 }
1222 else
1223 {
1224 if (pCopyResp->cp_private)
1225 freemsg(pCopyResp->cp_private);
1226 AssertReturn(enmDirection == OUT || enmDirection == BOTH, EINVAL);
1227 vbmsSolAcknowledgeIOCtl(pMBlk, 0, 0);
1228 return 0;
1229 }
1230}
1231
1232/**
1233 * Helper for vbmsSolHandleIOCtl. This rather complicated-looking
1234 * code is basically the standard boilerplate for handling transparent IOCtls,
1235 * that is, IOCtls which are not re-packed inside STREAMS IOCtls.
1236 * @copydoc vbmsSolHandleIOCtl
1237 */
1238int vbmsSolHandleTransparentIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
1239 PFNVBMSSOLIOCTL pfnHandler, int iCmd,
1240 size_t cbCmd,
1241 enum IOCTLDIRECTION enmDirection)
1242{
1243 int err = 0, rc = 0;
1244 size_t cbData = 0;
1245
1246 LogFlowFunc(("iCmd=0x%x, cbBuffer=%d, enmDirection=%d\n",
1247 (unsigned)iCmd, (int)cbCmd, (int)enmDirection));
1248 if ( (enmDirection != NONE && !pMBlk->b_cont)
1249 || enmDirection == UNSPECIFIED)
1250 return EINVAL;
1251 if (enmDirection == IN || enmDirection == BOTH)
1252 {
1253 void *pUserAddr = NULL;
1254 /* We only need state data if there is something to copy back. */
1255 if (enmDirection == BOTH)
1256 pUserAddr = *(void **)pMBlk->b_cont->b_rptr;
1257 mcopyin(pMBlk, pUserAddr /* state data */, cbCmd, NULL);
1258 }
1259 else if (enmDirection == OUT)
1260 {
1261 mblk_t *pMBlkOut = allocb(cbCmd, BPRI_MED);
1262 void *pvData;
1263
1264 if (!pMBlkOut)
1265 return EAGAIN;
1266 pvData = pMBlkOut->b_rptr;
1267 err = pfnHandler(pState, iCmd, pvData, cbCmd, &cbData, NULL);
1268 if (!err)
1269 mcopyout(pMBlk, NULL, cbData, NULL, pMBlkOut);
1270 else
1271 freemsg(pMBlkOut);
1272 }
1273 else
1274 {
1275 AssertReturn(enmDirection == NONE, EINVAL);
1276 err = pfnHandler(pState, iCmd, NULL, 0, NULL, &rc);
1277 if (!err)
1278 vbmsSolAcknowledgeIOCtl(pMBlk, 0, rc);
1279 }
1280 return err;
1281}
1282
1283/**
1284 * Helper for vbmsSolHandleIOCtl. This rather complicated-looking
1285 * code is basically the standard boilerplate for handling any streams IOCtl.
1286 * @copydoc vbmsSolHandleIOCtl
1287 */
1288static int vbmsSolHandleIStrIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
1289 PFNVBMSSOLIOCTL pfnHandler, int iCmd)
1290{
1291 struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
1292 uint_t cbBuffer = pIOCBlk->ioc_count;
1293 void *pvData = NULL;
1294 int err, rc = 0;
1295 size_t cbData = 0;
1296
1297 LogFlowFunc(("iCmd=0x%x, cbBuffer=%u, b_cont=%p\n",
1298 (unsigned)iCmd, cbBuffer, (void *)pMBlk->b_cont));
1299 if (cbBuffer && !pMBlk->b_cont)
1300 return EINVAL;
1301 /* Repack the whole buffer into a single message block if needed. */
1302 if (cbBuffer)
1303 {
1304 err = miocpullup(pMBlk, cbBuffer);
1305 if (err)
1306 return err;
1307 pvData = pMBlk->b_cont->b_rptr;
1308 }
1309 else if (pMBlk->b_cont) /* consms forgets to set ioc_count. */
1310 {
1311 pvData = pMBlk->b_cont->b_rptr;
1312 cbBuffer = pMBlk->b_cont->b_datap->db_lim
1313 - pMBlk->b_cont->b_datap->db_base;
1314 }
1315 err = pfnHandler(pState, iCmd, pvData, cbBuffer, &cbData, &rc);
1316 if (!err)
1317 {
1318 LogRelFlowFunc(("pMBlk=%p, pMBlk->b_datap=%p, pMBlk->b_rptr=%p\n",
1319 pMBlk, pMBlk->b_datap, pMBlk->b_rptr));
1320 vbmsSolAcknowledgeIOCtl(pMBlk, cbData, rc);
1321 }
1322 return err;
1323}
1324
1325
1326/**
1327 * Handle a VUID input device IOCtl.
1328 * @copydoc FNVBMSSOLIOCTL
1329 */
1330static int vbmsSolVUIDIOCtl(PVBMSSTATE pState, int iCmd, void *pvData,
1331 size_t cbBuffer, size_t *pcbData, int *prc)
1332{
1333 LogRelFlowFunc((DEVICE_NAME "::pvData=%p " /* no '\n' */, pvData));
1334 switch (iCmd)
1335 {
1336 case VUIDGFORMAT:
1337 {
1338 LogRelFlowFunc(("VUIDGFORMAT\n"));
1339 if (cbBuffer < sizeof(int))
1340 return EINVAL;
1341 *(int *)pvData = VUID_FIRM_EVENT;
1342 *pcbData = sizeof(int);
1343 return 0;
1344 }
1345 case VUIDSFORMAT:
1346 LogRelFlowFunc(("VUIDSFORMAT\n"));
1347 /* We define our native format to be VUID_FIRM_EVENT, so there
1348 * is nothing more to do and we exit here on success or on
1349 * failure. */
1350 return 0;
1351 case VUIDGADDR:
1352 case VUIDSADDR:
1353 LogRelFlowFunc(("VUIDGADDR/VUIDSADDR\n"));
1354 return ENOTTY;
1355 case MSIOGETPARMS:
1356 {
1357 Ms_parms parms = { 0 };
1358
1359 LogRelFlowFunc(("MSIOGETPARMS\n"));
1360 if (cbBuffer < sizeof(Ms_parms))
1361 return EINVAL;
1362 *(Ms_parms *)pvData = parms;
1363 *pcbData = sizeof(Ms_parms);
1364 return 0;
1365 }
1366 case MSIOSETPARMS:
1367 LogRelFlowFunc(("MSIOSETPARMS\n"));
1368 return 0;
1369 case MSIOSRESOLUTION:
1370 {
1371 Ms_screen_resolution *pResolution = (Ms_screen_resolution *)pvData;
1372 int rc;
1373
1374 LogRelFlowFunc(("MSIOSRESOLUTION, cbBuffer=%d, sizeof(Ms_screen_resolution)=%d\n",
1375 (int) cbBuffer,
1376 (int) sizeof(Ms_screen_resolution)));
1377 if (cbBuffer < sizeof(Ms_screen_resolution))
1378 return EINVAL;
1379 LogRelFlowFunc(("%dx%d\n", pResolution->width,
1380 pResolution->height));
1381 pState->cMaxScreenX = pResolution->width - 1;
1382 pState->cMaxScreenY = pResolution->height - 1;
1383 /* Note: we don't disable this again until session close. */
1384 rc = VbglR0SetMouseStatus( VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE
1385 | VMMDEV_MOUSE_NEW_PROTOCOL);
1386 if (RT_SUCCESS(rc))
1387 return 0;
1388 pState->cMaxScreenX = 0;
1389 pState->cMaxScreenY = 0;
1390 return ENODEV;
1391 }
1392 case MSIOBUTTONS:
1393 {
1394 LogRelFlowFunc(("MSIOBUTTONS\n"));
1395 if (cbBuffer < sizeof(int))
1396 return EINVAL;
1397 *(int *)pvData = 0;
1398 *pcbData = sizeof(int);
1399 return 0;
1400 }
1401 case VUIDGWHEELCOUNT:
1402 {
1403 LogRelFlowFunc(("VUIDGWHEELCOUNT\n"));
1404 if (cbBuffer < sizeof(int))
1405 return EINVAL;
1406 *(int *)pvData = 0;
1407 *pcbData = sizeof(int);
1408 return 0;
1409 }
1410 case VUIDGWHEELINFO:
1411 case VUIDGWHEELSTATE:
1412 case VUIDSWHEELSTATE:
1413 LogRelFlowFunc(("VUIDGWHEELINFO/VUIDGWHEELSTATE/VUIDSWHEELSTATE\n"));
1414 return EINVAL;
1415 default:
1416 LogRelFlowFunc(("Invalid IOCtl command %x\n", iCmd));
1417 return EINVAL;
1418 }
1419}
1420
1421
1422#ifdef TESTCASE
1423int main(void)
1424{
1425 RTTEST hTest;
1426 int rc = RTTestInitAndCreate("tstVBoxGuest-solaris", &hTest);
1427 if (rc)
1428 return rc;
1429 RTTestBanner(hTest);
1430 test_init(hTest);
1431 testOpenClose(hTest);
1432 testWPut(hTest);
1433
1434 /*
1435 * Summary.
1436 */
1437 return RTTestSummaryAndDestroy(hTest);
1438}
1439#endif
1440
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette