VirtualBox

source: vbox/trunk/src/VBox/Additions/solaris/Mouse/vboxmouse.c@ 42205

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

Additions/Solaris/vboxmouse: removed log message on every mouse event.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 50.6 KB
Line 
1/* $Id: vboxmouse.c 42205 2012-07-18 12:35:41Z vboxsync $ */
2/** @file
3 * VirtualBox Guest Additions Driver for Solaris.
4 */
5
6/*
7 * Copyright (C) 2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * 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#define LOG_GROUP LOG_GROUP_DRV_MOUSE
28
29/******************************************************************************
30* Header Files *
31******************************************************************************/
32
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 "vboxmouse"
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 LogRel((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 mutex_destroy(&g_OpenNodeState.InitMtx);
294
295 return rc;
296}
297
298
299/** Driver identification. */
300int _info(struct modinfo *pModInfo)
301{
302 int rc;
303 LogRelFlow((DEVICE_NAME ":_info\n"));
304 rc = mod_info(&g_vbmsSolModLinkage, pModInfo);
305 LogRelFlow((DEVICE_NAME ":_info returning %d\n", rc));
306 return rc;
307}
308
309
310/******************************************************************************
311* Initialisation entry points *
312******************************************************************************/
313
314/**
315 * Attach entry point, to attach a device to the system or resume it.
316 *
317 * @param pDip The module structure instance.
318 * @param enmCmd Attach type (ddi_attach_cmd_t)
319 *
320 * @return corresponding solaris error code.
321 */
322int vbmsSolAttach(dev_info_t *pDip, ddi_attach_cmd_t enmCmd)
323{
324 LogRelFlow((DEVICE_NAME "::Attach\n"));
325 switch (enmCmd)
326 {
327 case DDI_ATTACH:
328 {
329 int rc;
330 int instance = ddi_get_instance(pDip);
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, instance, DDI_PSEUDO, 0);
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 *ppvResult = (void *)g_OpenNodeState.pDip;
405 break;
406
407 case DDI_INFO_DEVT2INSTANCE:
408 *ppvResult = (void *)(uintptr_t)ddi_get_instance(g_OpenNodeState.pDip);
409 break;
410
411 default:
412 rc = DDI_FAILURE;
413 break;
414 }
415
416 NOREF(pvArg);
417 return rc;
418}
419
420
421/******************************************************************************
422* Main code *
423******************************************************************************/
424
425static void vbmsSolNotify(void *pvState);
426static void vbmsSolVUIDPutAbsEvent(PVBMSSTATE pState, ushort_t cEvent,
427 int cValue);
428
429/**
430 * Open callback for the read queue, which we use as a generic device open
431 * handler.
432 */
433int vbmsSolOpen(queue_t *pReadQueue, dev_t *pDev, int fFlag, int fMode,
434 cred_t *pCred)
435{
436 PVBMSSTATE pState = NULL;
437 int rc = VINF_SUCCESS;
438
439 NOREF(fFlag);
440 NOREF(pCred);
441 LogRelFlow((DEVICE_NAME "::Open\n"));
442
443 /*
444 * Initialize IPRT R0 driver, which internally calls OS-specific r0 init.
445 */
446 mutex_enter(&g_OpenNodeState.InitMtx);
447 if (!g_OpenNodeState.cInits)
448 {
449 rc = VbglInit();
450 if (RT_SUCCESS(rc))
451 {
452 rc = VbglGRAlloc((VMMDevRequestHeader **)
453 &g_OpenNodeState.pMouseStatusReq,
454 sizeof(*g_OpenNodeState.pMouseStatusReq),
455 VMMDevReq_GetMouseStatus);
456 if (RT_FAILURE(rc))
457 VbglTerminate();
458 }
459 }
460 ++g_OpenNodeState.cInits;
461 mutex_exit(&g_OpenNodeState.InitMtx);
462 if (RT_FAILURE(rc))
463 {
464 cmn_err(CE_NOTE, "_init: VbglInit failed. rc=%d\n", rc);
465 return EINVAL;
466 }
467 /*
468 * Sanity check on the mode parameter - only open as a driver, not a
469 * module, and we do cloning ourselves.
470 */
471 if (fMode)
472 {
473 LogRelFlow((DEVICE_NAME "::Open: invalid attempt to clone device."));
474 return EINVAL;
475 }
476
477 if (!ASMAtomicCmpXchgPtr(&g_OpenNodeState.pWriteQueue,
478 WR(pReadQueue), NULL))
479 {
480 LogRelFlow((DEVICE_NAME "::Open: only one instance allowed."));
481 return ENXIO;
482 }
483 pState = &g_OpenNodeState;
484
485 /*
486 * Create a new session.
487 */
488 if (VbglIsReady())
489 {
490 /* Initialise user data for the queues to our state and vice-versa. */
491 WR(pReadQueue)->q_ptr = (char *)pState;
492 pReadQueue->q_ptr = (char *)pState;
493 /* Enable our IRQ handler. */
494 VbglSetMouseNotifyCallback(vbmsSolNotify, (void *)pState);
495 qprocson(pReadQueue);
496 LogRel((DEVICE_NAME "::Open\n"));
497 return 0;
498 }
499
500 /* Failed, clean up. */
501 ASMAtomicWriteNullPtr(&pState->pWriteQueue);
502
503 LogRel((DEVICE_NAME "::Open: VBoxGuestCreateUserSession failed. rc=EFAULT\n"));
504 return EFAULT;
505}
506
507
508/**
509 * Notification callback, called when the VBoxGuest mouse pointer is moved.
510 * We send a VUID event up to user space. We may send a miscalculated event
511 * if a resolution change is half-way through, but that is pretty much to be
512 * expected, so we won't worry about it.
513 */
514void vbmsSolNotify(void *pvState)
515{
516 PVBMSSTATE pState = (PVBMSSTATE)pvState;
517 int rc;
518
519 pState->pMouseStatusReq->mouseFeatures = 0;
520 pState->pMouseStatusReq->pointerXPos = 0;
521 pState->pMouseStatusReq->pointerYPos = 0;
522 rc = VbglGRPerform(&pState->pMouseStatusReq->header);
523 if (RT_SUCCESS(rc))
524 {
525 int cMaxScreenX = pState->cMaxScreenX;
526 int cMaxScreenY = pState->cMaxScreenY;
527 int x = pState->pMouseStatusReq->pointerXPos;
528 int y = pState->pMouseStatusReq->pointerYPos;
529
530 if (cMaxScreenX && cMaxScreenY)
531 {
532 vbmsSolVUIDPutAbsEvent(pState, LOC_X_ABSOLUTE,
533 x * cMaxScreenX / VMMDEV_MOUSE_RANGE_MAX);
534 vbmsSolVUIDPutAbsEvent(pState, LOC_Y_ABSOLUTE,
535 y * cMaxScreenY / VMMDEV_MOUSE_RANGE_MAX);
536 }
537 }
538}
539
540
541void vbmsSolVUIDPutAbsEvent(PVBMSSTATE pState, ushort_t cEvent,
542 int cValue)
543{
544 queue_t *pReadQueue = RD(pState->pWriteQueue);
545 mblk_t *pMBlk = allocb(sizeof(Firm_event), BPRI_HI);
546 Firm_event *pEvent;
547 AssertReturnVoid(cEvent == LOC_X_ABSOLUTE || cEvent == LOC_Y_ABSOLUTE);
548 if (!pMBlk)
549 return; /* If kernel memory is short a missed event is acceptable! */
550 pEvent = (Firm_event *)pMBlk->b_wptr;
551 pEvent->id = cEvent;
552 pEvent->pair_type = FE_PAIR_DELTA;
553 pEvent->pair = cEvent == LOC_X_ABSOLUTE ? LOC_X_DELTA : LOC_Y_DELTA;
554 pEvent->value = cValue;
555 uniqtime32(&pEvent->time);
556 pMBlk->b_wptr += sizeof(Firm_event);
557 /* Put the message on the queue immediately if it is not blocked. */
558 if (canput(pReadQueue->q_next))
559 putnext(pReadQueue, pMBlk);
560 else
561 putq(pReadQueue, pMBlk);
562}
563
564
565/**
566 * Close callback for the read queue, which we use as a generic device close
567 * handler.
568 */
569int vbmsSolClose(queue_t *pReadQueue, int fFlag, cred_t *pCred)
570{
571 PVBMSSTATE pState = (PVBMSSTATE)pReadQueue->q_ptr;
572
573 LogRelFlow((DEVICE_NAME "::Close\n"));
574 NOREF(fFlag);
575 NOREF(pCred);
576
577 if (!pState)
578 {
579 Log((DEVICE_NAME "::Close: failed to get pState.\n"));
580 return EFAULT;
581 }
582
583 VbglSetMouseStatus(0);
584 /* Disable our IRQ handler. */
585 VbglSetMouseNotifyCallback(NULL, NULL);
586 qprocsoff(pReadQueue);
587
588 /*
589 * Close the session.
590 */
591 ASMAtomicWriteNullPtr(&pState->pWriteQueue);
592 pReadQueue->q_ptr = NULL;
593 mutex_enter(&g_OpenNodeState.InitMtx);
594 --g_OpenNodeState.cInits;
595 if (!g_OpenNodeState.cInits)
596 {
597 VbglGRFree(&g_OpenNodeState.pMouseStatusReq->header);
598 VbglTerminate();
599 }
600 mutex_exit(&g_OpenNodeState.InitMtx);
601 return 0;
602}
603
604
605#ifdef TESTCASE
606/** Simple test of vbmsSolOpen and vbmsSolClose. */
607static void testOpenClose(RTTEST hTest)
608{
609 queue_t aQueues[2];
610 dev_t device = 0;
611 int rc;
612
613 RTTestSub(hTest, "Testing vbmsSolOpen and vbmsSolClose");
614 RT_ZERO(g_OpenNodeState);
615 RT_ZERO(aQueues);
616 doInitQueues(&aQueues[0]);
617 rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
618 RTTEST_CHECK(hTest, rc == 0);
619 RTTEST_CHECK(hTest, g_OpenNodeState.pWriteQueue == WR(&aQueues[0]));
620 vbmsSolClose(RD(&aQueues[0]), 0, NULL);
621}
622#endif
623
624
625/* Helper for vbmsSolWPut. */
626static int vbmsSolDispatchIOCtl(PVBMSSTATE pState, mblk_t *pMBlk);
627
628/**
629 * Handler for messages sent from above (user-space and upper modules) which
630 * land in our write queue.
631 */
632int vbmsSolWPut(queue_t *pWriteQueue, mblk_t *pMBlk)
633{
634 LogRelFlowFunc((DEVICE_NAME "::"));
635 switch (pMBlk->b_datap->db_type)
636 {
637 case M_FLUSH:
638 LogRelFlow(("M_FLUSH, FLUSHW=%RTbool, FLUSHR=%RTbool\n",
639 *pMBlk->b_rptr & FLUSHW, *pMBlk->b_rptr & FLUSHR));
640 /* Flush the write queue if so requested. */
641 if (*pMBlk->b_rptr & FLUSHW)
642 flushq(pWriteQueue, FLUSHDATA);
643
644 /* Flush the read queue if so requested. */
645 if (*pMBlk->b_rptr & FLUSHR)
646 flushq(RD(pWriteQueue), FLUSHDATA);
647
648 /* We have no one below us to pass the message on to. */
649 return 0;
650 /* M_IOCDATA is additional data attached to (at least) transparent
651 * IOCtls. We handle the two together here and separate them further
652 * down. */
653 case M_IOCTL:
654 case M_IOCDATA:
655 {
656 PVBMSSTATE pState = (PVBMSSTATE)pWriteQueue->q_ptr;
657 int err;
658
659 LogRelFlow(( pMBlk->b_datap->db_type == M_IOCTL
660 ? "M_IOCTL\n" : "M_IOCDATA\n"));
661 err = vbmsSolDispatchIOCtl(pState, pMBlk);
662 if (!err)
663 qreply(pWriteQueue, pMBlk);
664 else
665 miocnak(pWriteQueue, pMBlk, 0, err);
666 break;
667 }
668 default:
669 LogRelFlow(("Unknown command, not acknowledging.\n"));
670 }
671 return 0;
672}
673
674
675#ifdef TESTCASE
676/* Constants, definitions and test functions for testWPut. */
677static const int g_ccTestWPutFirmEvent = VUID_FIRM_EVENT;
678# define PVGFORMAT (&g_ccTestWPutFirmEvent)
679# define CBGFORMAT (sizeof(g_ccTestWPutFirmEvent))
680static const Ms_screen_resolution g_TestResolution = { 640, 480 };
681# define PMSIOSRES (&g_TestResolution)
682# define CBMSIOSRES (sizeof(g_TestResolution))
683
684static inline bool testSetResolution(RTTEST hTest, queue_t *pWriteQueue,
685 struct msgb *pMBlk)
686{
687 PVBMSSTATE pState = (PVBMSSTATE)pWriteQueue->q_ptr;
688 RTTEST_CHECK_MSG_RET(hTest, pState->cMaxScreenX
689 == g_TestResolution.width - 1,
690 (hTest, "pState->cMaxScreenX=%d\n",
691 pState->cMaxScreenX), false);
692 RTTEST_CHECK_MSG_RET(hTest, pState->cMaxScreenY
693 == g_TestResolution.height - 1,
694 (hTest, "pState->cMaxScreenY=%d\n",
695 pState->cMaxScreenY), false);
696 return true;
697}
698
699/** Data table for testWPut. */
700static struct
701{
702 int iIOCCmd;
703 size_t cbData;
704 const void *pvDataIn;
705 size_t cbDataIn;
706 const void *pvDataOut;
707 size_t cbDataOut;
708 int rcExp;
709 bool (*pfnExtra)(RTTEST hTest, queue_t *pWriteQueue, struct msgb *pMBlk);
710 bool fCanTransparent;
711} g_asTestWPut[] =
712{
713 /* iIOCCmd cbData pvDataIn cbDataIn
714 pvDataOut cbDataOut rcExp pfnExtra fCanTransparent */
715 { VUIDGFORMAT, sizeof(int), NULL, 0,
716 PVGFORMAT, CBGFORMAT, 0, NULL, true },
717 { VUIDGFORMAT, sizeof(int) - 1, NULL, 0,
718 NULL, 0, EINVAL, NULL, false },
719 { VUIDGFORMAT, sizeof(int) + 1, NULL, 0,
720 PVGFORMAT, CBGFORMAT, 0, NULL, true },
721 { VUIDSFORMAT, sizeof(int), PVGFORMAT, CBGFORMAT,
722 NULL, 0, 0, NULL, true },
723 { MSIOSRESOLUTION, CBMSIOSRES, PMSIOSRES, CBMSIOSRES,
724 NULL, 0, 0, testSetResolution, true },
725 { VUIDGWHEELINFO, 0, NULL, 0,
726 NULL, 0, EINVAL, NULL, true }
727};
728
729# undef PVGFORMAT
730# undef CBGFORMAT
731# undef PMSIOSRES
732# undef CBMSIOSRES
733
734/* Helpers for testWPut. */
735static void testWPutStreams(RTTEST hTest, unsigned i);
736static void testWPutTransparent(RTTEST hTest, unsigned i);
737static void testWPutIOCDataIn(RTTEST hTest, unsigned i);
738static void testWPutIOCDataOut(RTTEST hTest, unsigned i);
739
740/** Test WPut's handling of different IOCtls, which is bulk of the logic in
741 * this file. */
742static void testWPut(RTTEST hTest)
743{
744 unsigned i;
745
746 RTTestSub(hTest, "Testing vbmsWPut");
747 for (i = 0; i < RT_ELEMENTS(g_asTestWPut); ++i)
748 {
749 AssertReturnVoid(g_asTestWPut[i].cbDataIn <= g_asTestWPut[i].cbData);
750 AssertReturnVoid(g_asTestWPut[i].cbDataOut <= g_asTestWPut[i].cbData);
751 testWPutStreams(hTest, i);
752 if (g_asTestWPut[i].fCanTransparent)
753 testWPutTransparent(hTest, i);
754 if (g_asTestWPut[i].fCanTransparent && g_asTestWPut[i].cbDataIn)
755 testWPutIOCDataIn(hTest, i);
756 if (g_asTestWPut[i].fCanTransparent && g_asTestWPut[i].cbDataOut)
757 testWPutIOCDataOut(hTest, i);
758 }
759}
760
761
762#define MSG_DATA_SIZE 1024
763
764/** Simulate sending a streams IOCtl to WPut with the parameters from table
765 * line @a i. */
766void testWPutStreams(RTTEST hTest, unsigned i)
767{
768 queue_t aQueues[2];
769 dev_t device = 0;
770 struct msgb *pMBlk = allocb(sizeof(struct iocblk), BPRI_MED);
771 struct msgb *pMBlkCont = allocb(MSG_DATA_SIZE, BPRI_MED);
772 struct iocblk *pIOCBlk = pMBlk ? (struct iocblk *)pMBlk->b_rptr : NULL;
773 int rc, cFormat = 0;
774
775 AssertReturnVoid(pMBlk);
776 AssertReturnVoidStmt(pMBlkCont, freemsg(pMBlk));
777 RT_ZERO(aQueues);
778 doInitQueues(&aQueues[0]);
779 rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
780 RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
781 RTTEST_CHECK_MSG(hTest, g_OpenNodeState.pWriteQueue
782 == WR(&aQueues[0]), (hTest, "i=%u\n", i));
783 pMBlk->b_datap->db_type = M_IOCTL;
784 pIOCBlk->ioc_cmd = g_asTestWPut[i].iIOCCmd;
785 pIOCBlk->ioc_count = g_asTestWPut[i].cbData;
786 AssertReturnVoid(g_asTestWPut[i].cbData <= MSG_DATA_SIZE);
787 memcpy(pMBlkCont->b_rptr, g_asTestWPut[i].pvDataIn,
788 g_asTestWPut[i].cbDataIn);
789 pMBlk->b_cont = pMBlkCont;
790 rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
791 RTTEST_CHECK_MSG(hTest, pIOCBlk->ioc_error == g_asTestWPut[i].rcExp,
792 (hTest, "i=%u, IOCBlk.ioc_error=%d\n", i,
793 pIOCBlk->ioc_error));
794 RTTEST_CHECK_MSG(hTest, pIOCBlk->ioc_count == g_asTestWPut[i].cbDataOut,
795 (hTest, "i=%u, ioc_count=%u\n", i, pIOCBlk->ioc_count));
796 RTTEST_CHECK_MSG(hTest, !memcmp(pMBlkCont->b_rptr,
797 g_asTestWPut[i].pvDataOut,
798 g_asTestWPut[i].cbDataOut),
799 (hTest, "i=%u\n", i));
800 /* Hack to ensure that miocpullup() gets called when needed. */
801 if (g_asTestWPut[i].cbData > 0)
802 RTTEST_CHECK_MSG(hTest, pMBlk->b_flag == 1, (hTest, "i=%u\n", i));
803 if (!g_asTestWPut[i].rcExp)
804 RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
805 (hTest, "i=%u\n", i));
806 if (g_asTestWPut[i].pfnExtra)
807 if (!g_asTestWPut[i].pfnExtra(hTest, WR(&aQueues[0]), pMBlk))
808 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Called from %s.\n",
809 __PRETTY_FUNCTION__);
810 vbmsSolClose(RD(&aQueues[1]), 0, NULL);
811 freemsg(pMBlk);
812}
813
814
815#define USER_ADDRESS 0xfeedbacc
816
817/** Simulate sending a transparent IOCtl to WPut with the parameters from table
818 * line @a i. */
819void testWPutTransparent(RTTEST hTest, unsigned i)
820{
821 queue_t aQueues[2];
822 dev_t device = 0;
823 struct msgb *pMBlk = allocb(sizeof(struct iocblk), BPRI_MED);
824 struct msgb *pMBlkCont = allocb(sizeof(void *), BPRI_MED);
825 struct iocblk *pIOCBlk = pMBlk ? (struct iocblk *)pMBlk->b_rptr : NULL;
826 struct copyreq *pCopyReq;
827 int rc, cFormat = 0;
828
829 /* if (g_asTestWPut[i].cbDataIn == 0 && g_asTestWPut[i].cbDataOut != 0)
830 return; */ /* This case will be handled once the current ones work. */
831 AssertReturnVoid(pMBlk);
832 AssertReturnVoidStmt(pMBlkCont, freemsg(pMBlk));
833 RT_ZERO(aQueues);
834 doInitQueues(&aQueues[0]);
835 rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
836 RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
837 RTTEST_CHECK_MSG(hTest, g_OpenNodeState.pWriteQueue
838 == WR(&aQueues[0]), (hTest, "i=%u\n", i));
839 pMBlk->b_datap->db_type = M_IOCTL;
840 pIOCBlk->ioc_cmd = g_asTestWPut[i].iIOCCmd;
841 pIOCBlk->ioc_count = TRANSPARENT;
842 *(void **)pMBlkCont->b_rptr = (void *)USER_ADDRESS;
843 pMBlk->b_cont = pMBlkCont;
844 rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
845 pCopyReq = (struct copyreq *)pMBlk->b_rptr;
846 RTTEST_CHECK_MSG(hTest, ( ( g_asTestWPut[i].cbDataIn
847 && (pMBlk->b_datap->db_type == M_COPYIN))
848 || ( g_asTestWPut[i].cbDataOut
849 && (pMBlk->b_datap->db_type == M_COPYOUT))
850 || ( (g_asTestWPut[i].rcExp == 0)
851 && pMBlk->b_datap->db_type == M_IOCACK)
852 || (pMBlk->b_datap->db_type == M_IOCNAK)),
853 (hTest, "i=%u, db_type=%u\n", i,
854 (unsigned) pMBlk->b_datap->db_type));
855 /* Our TRANSPARENT IOCtls can only return non-zero if they have no payload.
856 * Others should either return zero or be non-TRANSPARENT only. */
857 if (pMBlk->b_datap->db_type == M_IOCNAK)
858 RTTEST_CHECK_MSG(hTest, pIOCBlk->ioc_error == g_asTestWPut[i].rcExp,
859 (hTest, "i=%u, IOCBlk.ioc_error=%d\n", i,
860 pIOCBlk->ioc_error));
861 if (g_asTestWPut[i].cbData)
862 {
863 RTTEST_CHECK_MSG(hTest, pCopyReq->cq_addr == (char *)USER_ADDRESS,
864 (hTest, "i=%u, cq_addr=%p\n", i, pCopyReq->cq_addr));
865 RTTEST_CHECK_MSG( hTest, pCopyReq->cq_size
866 == g_asTestWPut[i].cbDataIn
867 ? g_asTestWPut[i].cbDataIn
868 : g_asTestWPut[i].cbDataOut,
869 (hTest, "i=%u, cq_size=%llu\n", i,
870 (unsigned long long)pCopyReq->cq_size));
871 }
872 /* Implementation detail - check that the private pointer is correctly
873 * set to the user address *for two direction IOCtls* or NULL otherwise. */
874 if (g_asTestWPut[i].cbDataIn && g_asTestWPut[i].cbDataOut)
875 RTTEST_CHECK_MSG(hTest, pCopyReq->cq_private == (mblk_t *)USER_ADDRESS,
876 (hTest, "i=%u, cq_private=%p\n", i,
877 pCopyReq->cq_private));
878 else if ( (pMBlk->b_datap->db_type == M_COPYIN)
879 || (pMBlk->b_datap->db_type == M_COPYOUT))
880 RTTEST_CHECK_MSG(hTest, !pCopyReq->cq_private,
881 (hTest, "i=%u, cq_private=%p\n", i,
882 pCopyReq->cq_private));
883 if (!g_asTestWPut[i].rcExp)
884 RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
885 (hTest, "i=%u\n", i));
886 if (g_asTestWPut[i].pfnExtra && !g_asTestWPut[i].cbData)
887 if (!g_asTestWPut[i].pfnExtra(hTest, WR(&aQueues[0]), pMBlk))
888 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Called from %s.\n",
889 __PRETTY_FUNCTION__);
890 vbmsSolClose(RD(&aQueues[1]), 0, NULL);
891 freemsg(pMBlk);
892}
893
894
895/** Simulate sending follow-on IOCData messages to a transparent IOCtl to WPut
896 * with the parameters from table line @a i. */
897void testWPutIOCDataIn(RTTEST hTest, unsigned i)
898{
899 queue_t aQueues[2];
900 dev_t device = 0;
901 struct msgb *pMBlk = allocb(sizeof(struct copyresp), BPRI_MED);
902 struct msgb *pMBlkCont = allocb(MSG_DATA_SIZE, BPRI_MED);
903 struct copyresp *pCopyResp = pMBlk ? (struct copyresp *)pMBlk->b_rptr
904 : NULL;
905 void *pvData = pMBlkCont ? pMBlkCont->b_rptr : NULL;
906 struct copyreq *pCopyReq;
907 int rc, cFormat = 0;
908
909 AssertReturnVoid(pMBlk);
910 AssertReturnVoidStmt(pMBlkCont, freemsg(pMBlk));
911 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "%s: i=%u\n", __PRETTY_FUNCTION__,
912 i);
913 AssertReturnVoidStmt(g_asTestWPut[i].cbDataIn, freemsg(pMBlk));
914 RT_ZERO(aQueues);
915 doInitQueues(&aQueues[0]);
916 rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
917 RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
918 RTTEST_CHECK_MSG(hTest, g_OpenNodeState.pWriteQueue
919 == WR(&aQueues[0]), (hTest, "i=%u\n", i));
920 pMBlk->b_datap->db_type = M_IOCDATA;
921 pCopyResp->cp_cmd = g_asTestWPut[i].iIOCCmd;
922 if (g_asTestWPut[i].cbDataOut)
923 pCopyResp->cp_private = USER_ADDRESS;
924 AssertReturnVoid(g_asTestWPut[i].cbData <= MSG_DATA_SIZE);
925 memcpy(pMBlkCont->b_rptr, g_asTestWPut[i].pvDataIn, g_asTestWPut[i].cbDataIn);
926 pMBlk->b_cont = pMBlkCont;
927 rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
928 pCopyReq = (struct copyreq *)pMBlk->b_rptr;
929 RTTEST_CHECK_MSG(hTest, ( ( g_asTestWPut[i].cbDataOut
930 && (pMBlk->b_datap->db_type == M_COPYOUT))
931 || ( (g_asTestWPut[i].rcExp == 0)
932 && pMBlk->b_datap->db_type == M_IOCACK)
933 || (pMBlk->b_datap->db_type == M_IOCNAK)),
934 (hTest, "i=%u, db_type=%u\n", i,
935 (unsigned) pMBlk->b_datap->db_type));
936 if (g_asTestWPut[i].cbDataOut)
937 {
938 RTTEST_CHECK_MSG(hTest, pCopyReq->cq_addr == (char *)pvData,
939 (hTest, "i=%u, cq_addr=%p\n", i, pCopyReq->cq_addr));
940 RTTEST_CHECK_MSG(hTest, pCopyReq->cq_size == g_asTestWPut[i].cbData,
941 (hTest, "i=%u, cq_size=%llu\n", i,
942 (unsigned long long)pCopyReq->cq_size));
943 RTTEST_CHECK_MSG(hTest, !memcmp(pvData, g_asTestWPut[i].pvDataOut,
944 g_asTestWPut[i].cbDataOut),
945 (hTest, "i=%u\n", i));
946 }
947 RTTEST_CHECK_MSG(hTest, !pCopyReq->cq_private,
948 (hTest, "i=%u, cq_private=%p\n", i,
949 pCopyReq->cq_private));
950 if (!g_asTestWPut[i].rcExp)
951 RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
952 (hTest, "i=%u\n", i));
953 if (g_asTestWPut[i].pfnExtra && !g_asTestWPut[i].cbDataOut)
954 if (!g_asTestWPut[i].pfnExtra(hTest, WR(&aQueues[0]), pMBlk))
955 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "Called from %s.\n",
956 __PRETTY_FUNCTION__);
957 vbmsSolClose(RD(&aQueues[1]), 0, NULL);
958 freemsg(pMBlk);
959}
960
961
962/** Simulate sending follow-on IOCData messages to a transparent IOCtl to WPut
963 * with the parameters from table line @a i. */
964void testWPutIOCDataOut(RTTEST hTest, unsigned i)
965{
966 queue_t aQueues[2];
967 dev_t device = 0;
968 struct msgb *pMBlk = allocb(sizeof(struct copyresp), BPRI_MED);
969 struct copyresp *pCopyResp = pMBlk ? (struct copyresp *)pMBlk->b_rptr
970 : NULL;
971 int rc, cFormat = 0;
972
973 AssertReturnVoid(pMBlk);
974 AssertReturnVoidStmt(g_asTestWPut[i].cbDataOut, freemsg(pMBlk));
975 RTTestPrintf(hTest, RTTESTLVL_ALWAYS, "%s: i=%u\n", __PRETTY_FUNCTION__,
976 i);
977 RT_ZERO(aQueues);
978 doInitQueues(&aQueues[0]);
979 rc = vbmsSolOpen(RD(&aQueues[0]), &device, 0, 0, NULL);
980 RTTEST_CHECK_MSG(hTest, rc == 0, (hTest, "i=%u, rc=%d\n", i, rc));
981 RTTEST_CHECK_MSG(hTest, g_OpenNodeState.pWriteQueue
982 == WR(&aQueues[0]), (hTest, "i=%u\n", i));
983 pMBlk->b_datap->db_type = M_IOCDATA;
984 pCopyResp->cp_cmd = g_asTestWPut[i].iIOCCmd;
985 rc = vbmsSolWPut(WR(&aQueues[0]), pMBlk);
986 RTTEST_CHECK_MSG(hTest, pMBlk->b_datap->db_type == M_IOCACK,
987 (hTest, "i=%u, db_type=%u\n", i,
988 (unsigned) pMBlk->b_datap->db_type));
989 if (!g_asTestWPut[i].rcExp)
990 RTTEST_CHECK_MSG(hTest, RD(&aQueues[0])->q_first == pMBlk,
991 (hTest, "i=%u\n", i));
992 vbmsSolClose(RD(&aQueues[1]), 0, NULL);
993 freemsg(pMBlk);
994}
995#endif
996
997
998/** Data transfer direction of an IOCtl. This is used for describing
999 * transparent IOCtls, and @a UNSPECIFIED is not a valid value for them. */
1000enum IOCTLDIRECTION
1001{
1002 /** This IOCtl transfers no data. */
1003 NONE,
1004 /** This IOCtl only transfers data from user to kernel. */
1005 IN,
1006 /** This IOCtl only transfers data from kernel to user. */
1007 OUT,
1008 /** This IOCtl transfers data from user to kernel and back. */
1009 BOTH,
1010 /** We aren't saying anything about how the IOCtl transfers data. */
1011 UNSPECIFIED
1012};
1013
1014/**
1015 * IOCtl handler function.
1016 * @returns 0 on success, error code on failure.
1017 * @param iCmd The IOCtl command number.
1018 * @param pvData Buffer for the user data.
1019 * @param cbBuffer Size of the buffer in @a pvData or zero.
1020 * @param pcbData Where to set the size of the data returned. Required for
1021 * handlers which return data.
1022 * @param prc Where to store the return code. Default is zero. Only
1023 * used for IOCtls without data for convenience of
1024 * implemention.
1025 */
1026typedef int FNVBMSSOLIOCTL(PVBMSSTATE pState, int iCmd, void *pvData,
1027 size_t cbBuffer, size_t *pcbData, int *prc);
1028typedef FNVBMSSOLIOCTL *PFNVBMSSOLIOCTL;
1029
1030/* Helpers for vbmsSolDispatchIOCtl. */
1031static int vbmsSolHandleIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
1032 PFNVBMSSOLIOCTL pfnHandler,
1033 int iCmd, size_t cbCmd,
1034 enum IOCTLDIRECTION enmDirection);
1035static int vbmsSolVUIDIOCtl(PVBMSSTATE pState, int iCmd, void *pvData,
1036 size_t cbBuffer, size_t *pcbData, int *prc);
1037
1038/** Table of supported VUID IOCtls. */
1039struct
1040{
1041 /** The IOCtl number. */
1042 int iCmd;
1043 /** The size of the buffer which needs to be copied between user and kernel
1044 * space, or zero if unknown (must be known for tranparent IOCtls). */
1045 size_t cbBuffer;
1046 /** The direction the buffer data needs to be copied. This must be
1047 * specified for transparent IOCtls. */
1048 enum IOCTLDIRECTION enmDirection;
1049} g_aVUIDIOCtlDescriptions[] =
1050{
1051 { VUIDGFORMAT, sizeof(int), OUT },
1052 { VUIDSFORMAT, sizeof(int), IN },
1053 { VUIDGADDR, 0, UNSPECIFIED },
1054 { VUIDGADDR, 0, UNSPECIFIED },
1055 { MSIOGETPARMS, sizeof(Ms_parms), OUT },
1056 { MSIOSETPARMS, sizeof(Ms_parms), IN },
1057 { MSIOSRESOLUTION, sizeof(Ms_screen_resolution), IN },
1058 { MSIOBUTTONS, sizeof(int), OUT },
1059 { VUIDGWHEELCOUNT, sizeof(int), OUT },
1060 { VUIDGWHEELINFO, 0, UNSPECIFIED },
1061 { VUIDGWHEELSTATE, 0, UNSPECIFIED },
1062 { VUIDSWHEELSTATE, 0, UNSPECIFIED }
1063};
1064
1065/**
1066 * Handle a STREAMS IOCtl message for our driver on the write stream. This
1067 * function takes care of the IOCtl logic only and does not call qreply() or
1068 * miocnak() at all - the caller must call these on success or failure
1069 * respectively.
1070 * @returns 0 on success or the IOCtl error code on failure.
1071 * @param pState pointer to the state structure.
1072 * @param pMBlk pointer to the STREAMS message block structure.
1073 */
1074static int vbmsSolDispatchIOCtl(PVBMSSTATE pState, mblk_t *pMBlk)
1075{
1076 struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
1077 int iCmd = pIOCBlk->ioc_cmd, iCmdType = iCmd & (0xff << 8);
1078 size_t cbBuffer;
1079 enum IOCTLDIRECTION enmDirection;
1080
1081 LogRelFlowFunc((DEVICE_NAME "::pIOCBlk=%p, iCmdType=%c, iCmd=0x%x\n",
1082 pIOCBlk, (char) (iCmdType >> 8), (unsigned)iCmd));
1083 switch (iCmdType)
1084 {
1085 case MSIOC:
1086 case VUIOC:
1087 {
1088 unsigned i;
1089
1090 for (i = 0; i < RT_ELEMENTS(g_aVUIDIOCtlDescriptions); ++i)
1091 if (g_aVUIDIOCtlDescriptions[i].iCmd == iCmd)
1092 {
1093 cbBuffer = g_aVUIDIOCtlDescriptions[i].cbBuffer;
1094 enmDirection = g_aVUIDIOCtlDescriptions[i].enmDirection;
1095 return vbmsSolHandleIOCtl(pState, pMBlk,
1096 vbmsSolVUIDIOCtl, iCmd,
1097 cbBuffer, enmDirection);
1098 }
1099 return EINVAL;
1100 }
1101 default:
1102 return ENOTTY;
1103 }
1104}
1105
1106
1107/* Helpers for vbmsSolHandleIOCtl. */
1108static int vbmsSolHandleIOCtlData(PVBMSSTATE pState, mblk_t *pMBlk,
1109 PFNVBMSSOLIOCTL pfnHandler, int iCmd,
1110 size_t cbCmd,
1111 enum IOCTLDIRECTION enmDirection);
1112
1113static int vbmsSolHandleTransparentIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
1114 PFNVBMSSOLIOCTL pfnHandler,
1115 int iCmd, size_t cbCmd,
1116 enum IOCTLDIRECTION enmDirection);
1117
1118static int vbmsSolHandleIStrIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
1119 PFNVBMSSOLIOCTL pfnHandler, int iCmd);
1120
1121static void vbmsSolAcknowledgeIOCtl(mblk_t *pMBlk, int cbData, int rc)
1122{
1123 struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
1124
1125 pMBlk->b_datap->db_type = M_IOCACK;
1126 pIOCBlk->ioc_count = cbData;
1127 pIOCBlk->ioc_rval = rc;
1128 pIOCBlk->ioc_error = 0;
1129}
1130
1131/**
1132 * Generic code for handling STREAMS-specific IOCtl logic and boilerplate. It
1133 * calls the IOCtl handler passed to it without the handler having to be aware
1134 * of STREAMS structures, or whether this is a transparent (traditional) or an
1135 * I_STR (using a STREAMS structure to describe the data) IOCtl. With the
1136 * caveat that we only support transparent IOCtls which pass all data in a
1137 * single buffer of a fixed size (I_STR IOCtls are restricted to a single
1138 * buffer anyway, but the caller can choose the buffer size).
1139 * @returns 0 on success or the IOCtl error code on failure.
1140 * @param pState pointer to the state structure.
1141 * @param pMBlk pointer to the STREAMS message block structure.
1142 * @param pfnHandler pointer to the right IOCtl handler function for this
1143 * IOCtl number.
1144 * @param iCmd IOCtl command number.
1145 * @param cbCmd size of the user space buffer for this IOCtl number,
1146 * used for processing transparent IOCtls. Pass zero
1147 * for IOCtls with no maximum buffer size (which will
1148 * not be able to be handled as transparent) or with
1149 * no argument.
1150 * @param enmDirection data transfer direction of the IOCtl.
1151 */
1152static int vbmsSolHandleIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
1153 PFNVBMSSOLIOCTL pfnHandler, int iCmd,
1154 size_t cbCmd, enum IOCTLDIRECTION enmDirection)
1155{
1156 struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
1157
1158 LogFlowFunc(("iCmd=0x%x, cbBuffer=%d, enmDirection=%d\n",
1159 (unsigned)iCmd, (int)cbCmd, (int)enmDirection));
1160 if (pMBlk->b_datap->db_type == M_IOCDATA)
1161 return vbmsSolHandleIOCtlData(pState, pMBlk, pfnHandler, iCmd,
1162 cbCmd, enmDirection);
1163 else if ( pMBlk->b_datap->db_type == M_IOCTL
1164 && pIOCBlk->ioc_count == TRANSPARENT)
1165 return vbmsSolHandleTransparentIOCtl(pState, pMBlk, pfnHandler,
1166 iCmd, cbCmd, enmDirection);
1167 else if (pMBlk->b_datap->db_type == M_IOCTL)
1168 return vbmsSolHandleIStrIOCtl(pState, pMBlk, pfnHandler, iCmd);
1169 return EINVAL;
1170}
1171
1172
1173/**
1174 * Helper for vbmsSolHandleIOCtl. This rather complicated-looking
1175 * code is basically the standard boilerplate for handling any streams IOCtl
1176 * additional data, which we currently only use for transparent IOCtls.
1177 * @copydoc vbmsSolHandleIOCtl
1178 */
1179static int vbmsSolHandleIOCtlData(PVBMSSTATE pState, mblk_t *pMBlk,
1180 PFNVBMSSOLIOCTL pfnHandler, int iCmd,
1181 size_t cbCmd,
1182 enum IOCTLDIRECTION enmDirection)
1183{
1184 struct copyresp *pCopyResp = (struct copyresp *)pMBlk->b_rptr;
1185
1186 LogFlowFunc(("iCmd=0x%x, cbBuffer=%d, enmDirection=%d, cp_rval=%d, cp_private=%p\n",
1187 (unsigned)iCmd, (int)cbCmd, (int)enmDirection,
1188 (int)(uintptr_t)pCopyResp->cp_rval,
1189 (void *)pCopyResp->cp_private));
1190 if (pCopyResp->cp_rval) /* cp_rval is a pointer used as a boolean. */
1191 {
1192 freemsg(pMBlk);
1193 return EAGAIN;
1194 }
1195 if ((pCopyResp->cp_private && enmDirection == BOTH) || enmDirection == IN)
1196 {
1197 size_t cbData = 0;
1198 void *pvData = NULL;
1199 int err;
1200
1201 if (!pMBlk->b_cont)
1202 return EINVAL;
1203 if (enmDirection == BOTH && !pCopyResp->cp_private)
1204 return EINVAL;
1205 pvData = pMBlk->b_cont->b_rptr;
1206 err = pfnHandler(pState, iCmd, pvData, cbCmd, &cbData, NULL);
1207 if (!err && enmDirection == BOTH)
1208 mcopyout(pMBlk, NULL, cbData, pCopyResp->cp_private, NULL);
1209 else if (!err && enmDirection == IN)
1210 vbmsSolAcknowledgeIOCtl(pMBlk, 0, 0);
1211 return err;
1212 }
1213 else
1214 {
1215 AssertReturn(enmDirection == OUT || enmDirection == BOTH, EINVAL);
1216 vbmsSolAcknowledgeIOCtl(pMBlk, 0, 0);
1217 return 0;
1218 }
1219}
1220
1221/**
1222 * Helper for vbmsSolHandleIOCtl. This rather complicated-looking
1223 * code is basically the standard boilerplate for handling transparent IOCtls,
1224 * that is, IOCtls which are not re-packed inside STREAMS IOCtls.
1225 * @copydoc vbmsSolHandleIOCtl
1226 */
1227int vbmsSolHandleTransparentIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
1228 PFNVBMSSOLIOCTL pfnHandler, int iCmd,
1229 size_t cbCmd,
1230 enum IOCTLDIRECTION enmDirection)
1231{
1232 int err = 0, rc = 0;
1233 size_t cbData = 0;
1234
1235 LogFlowFunc(("iCmd=0x%x, cbBuffer=%d, enmDirection=%d\n",
1236 (unsigned)iCmd, (int)cbCmd, (int)enmDirection));
1237 if ( (enmDirection != NONE && !pMBlk->b_cont)
1238 || enmDirection == UNSPECIFIED)
1239 return EINVAL;
1240 if (enmDirection == IN || enmDirection == BOTH)
1241 {
1242 void *pUserAddr = NULL;
1243 /* We only need state data if there is something to copy back. */
1244 if (enmDirection == BOTH)
1245 pUserAddr = *(void **)pMBlk->b_cont->b_rptr;
1246 mcopyin(pMBlk, pUserAddr /* state data */, cbCmd, NULL);
1247 }
1248 else if (enmDirection == OUT)
1249 {
1250 mblk_t *pMBlkOut = allocb(cbCmd, BPRI_MED);
1251 void *pvData;
1252
1253 if (!pMBlkOut)
1254 return EAGAIN;
1255 pvData = pMBlkOut->b_rptr;
1256 err = pfnHandler(pState, iCmd, pvData, cbCmd, &cbData, NULL);
1257 if (!err)
1258 mcopyout(pMBlk, NULL, cbData, NULL, pMBlkOut);
1259 else
1260 freemsg(pMBlkOut);
1261 }
1262 else
1263 {
1264 AssertReturn(enmDirection == NONE, EINVAL);
1265 err = pfnHandler(pState, iCmd, NULL, 0, NULL, &rc);
1266 if (!err)
1267 vbmsSolAcknowledgeIOCtl(pMBlk, 0, rc);
1268 }
1269 return err;
1270}
1271
1272/**
1273 * Helper for vbmsSolHandleIOCtl. This rather complicated-looking
1274 * code is basically the standard boilerplate for handling any streams IOCtl.
1275 * @copydoc vbmsSolHandleIOCtl
1276 */
1277static int vbmsSolHandleIStrIOCtl(PVBMSSTATE pState, mblk_t *pMBlk,
1278 PFNVBMSSOLIOCTL pfnHandler, int iCmd)
1279{
1280 struct iocblk *pIOCBlk = (struct iocblk *)pMBlk->b_rptr;
1281 uint_t cbBuffer = pIOCBlk->ioc_count;
1282 void *pvData = NULL;
1283 int err, rc = 0;
1284 size_t cbData = 0;
1285
1286 LogFlowFunc(("iCmd=0x%x, cbBuffer=%u, b_cont=%p\n",
1287 (unsigned)iCmd, cbBuffer, (void *)pMBlk->b_cont));
1288 if (cbBuffer && !pMBlk->b_cont)
1289 return EINVAL;
1290 /* Repack the whole buffer into a single message block if needed. */
1291 if (cbBuffer)
1292 {
1293 err = miocpullup(pMBlk, cbBuffer);
1294 if (err)
1295 return err;
1296 pvData = pMBlk->b_cont->b_rptr;
1297 }
1298 else if (pMBlk->b_cont) /* consms forgets to set ioc_count. */
1299 {
1300 pvData = pMBlk->b_cont->b_rptr;
1301 cbBuffer = pMBlk->b_cont->b_datap->db_lim
1302 - pMBlk->b_cont->b_datap->db_base;
1303 }
1304 err = pfnHandler(pState, iCmd, pvData, cbBuffer, &cbData, &rc);
1305 if (!err)
1306 {
1307 LogRelFlowFunc(("pMBlk=%p, pMBlk->b_datap=%p, pMBlk->b_rptr=%p\n",
1308 pMBlk, pMBlk->b_datap, pMBlk->b_rptr));
1309 vbmsSolAcknowledgeIOCtl(pMBlk, cbData, rc);
1310 }
1311 return err;
1312}
1313
1314
1315/**
1316 * Handle a VUID input device IOCtl.
1317 * @copydoc FNVBMSSOLIOCTL
1318 */
1319static int vbmsSolVUIDIOCtl(PVBMSSTATE pState, int iCmd, void *pvData,
1320 size_t cbBuffer, size_t *pcbData, int *prc)
1321{
1322 LogRelFlowFunc((DEVICE_NAME "::pvData=%p " /* no '\n' */, pvData));
1323 switch (iCmd)
1324 {
1325 case VUIDGFORMAT:
1326 {
1327 LogRelFlowFunc(("VUIDGFORMAT\n"));
1328 if (cbBuffer < sizeof(int))
1329 return EINVAL;
1330 *(int *)pvData = VUID_FIRM_EVENT;
1331 *pcbData = sizeof(int);
1332 return 0;
1333 }
1334 case VUIDSFORMAT:
1335 LogRelFlowFunc(("VUIDSFORMAT\n"));
1336 /* We define our native format to be VUID_FIRM_EVENT, so there
1337 * is nothing more to do and we exit here on success or on
1338 * failure. */
1339 return 0;
1340 case VUIDGADDR:
1341 case VUIDSADDR:
1342 LogRelFlowFunc(("VUIDGADDR/VUIDSADDR\n"));
1343 return ENOTTY;
1344 case MSIOGETPARMS:
1345 {
1346 Ms_parms parms = { 0 };
1347
1348 LogRelFlowFunc(("MSIOGETPARMS\n"));
1349 if (cbBuffer < sizeof(Ms_parms))
1350 return EINVAL;
1351 *(Ms_parms *)pvData = parms;
1352 *pcbData = sizeof(Ms_parms);
1353 return 0;
1354 }
1355 case MSIOSETPARMS:
1356 LogRelFlowFunc(("MSIOSETPARMS\n"));
1357 return 0;
1358 case MSIOSRESOLUTION:
1359 {
1360 Ms_screen_resolution *pResolution = (Ms_screen_resolution *)pvData;
1361 int rc;
1362
1363 LogRelFlowFunc(("MSIOSRESOLUTION, cbBuffer=%d, sizeof(Ms_screen_resolution)=%d\n",
1364 (int) cbBuffer,
1365 (int) sizeof(Ms_screen_resolution)));
1366 if (cbBuffer < sizeof(Ms_screen_resolution))
1367 return EINVAL;
1368 LogRelFlowFunc(("%dx%d\n", pResolution->width,
1369 pResolution->height));
1370 pState->cMaxScreenX = pResolution->width - 1;
1371 pState->cMaxScreenY = pResolution->height - 1;
1372 /* Note: we don't disable this again until session close. */
1373 rc = VbglSetMouseStatus( VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE
1374 | VMMDEV_MOUSE_NEW_PROTOCOL);
1375 if (RT_SUCCESS(rc))
1376 return 0;
1377 pState->cMaxScreenX = 0;
1378 pState->cMaxScreenY = 0;
1379 return ENODEV;
1380 }
1381 case MSIOBUTTONS:
1382 {
1383 LogRelFlowFunc(("MSIOBUTTONS\n"));
1384 if (cbBuffer < sizeof(int))
1385 return EINVAL;
1386 *(int *)pvData = 0;
1387 *pcbData = sizeof(int);
1388 return 0;
1389 }
1390 case VUIDGWHEELCOUNT:
1391 {
1392 LogRelFlowFunc(("VUIDGWHEELCOUNT\n"));
1393 if (cbBuffer < sizeof(int))
1394 return EINVAL;
1395 *(int *)pvData = 0;
1396 *pcbData = sizeof(int);
1397 return 0;
1398 }
1399 case VUIDGWHEELINFO:
1400 case VUIDGWHEELSTATE:
1401 case VUIDSWHEELSTATE:
1402 LogRelFlowFunc(("VUIDGWHEELINFO/VUIDGWHEELSTATE/VUIDSWHEELSTATE\n"));
1403 return EINVAL;
1404 default:
1405 LogRelFlowFunc(("Invalid IOCtl command %x\n", iCmd));
1406 return EINVAL;
1407 }
1408}
1409
1410
1411#ifdef TESTCASE
1412int main(void)
1413{
1414 RTTEST hTest;
1415 int rc = RTTestInitAndCreate("tstVBoxGuest-solaris", &hTest);
1416 if (rc)
1417 return rc;
1418 RTTestBanner(hTest);
1419 test_init(hTest);
1420 testOpenClose(hTest);
1421 testWPut(hTest);
1422
1423 /*
1424 * Summary.
1425 */
1426 return RTTestSummaryAndDestroy(hTest);
1427}
1428#endif
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