VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 7 weeks ago

Copyright year updates by scm.

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