VirtualBox

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

Last change on this file since 68550 was 68550, checked in by vboxsync, 7 years ago

merging vbglioc r117689: Initial VBoxGuest I/O control changes.

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

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