VirtualBox

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

Last change on this file since 57358 was 57358, checked in by vboxsync, 10 years ago

*: scm cleanup run.

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