VirtualBox

source: vbox/trunk/src/VBox/ExtPacks/BusMouseSample/DevBusMouse.cpp

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.1 KB
RevLine 
[35683]1/* $Id: DevBusMouse.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
[35682]2/** @file
3 * BusMouse - Microsoft Bus (parallel) mouse controller device.
4 */
5
6/*
[98103]7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[35682]8 *
[35683]9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without
12 * restriction, including without limitation the rights to use,
13 * copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following
16 * conditions:
17 *
18 * The above copyright notice and this permission notice shall be
19 * included in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
[69275]24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
[35683]25 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 * OTHER DEALINGS IN THE SOFTWARE.
[35682]29 */
30
[57358]31
32/*********************************************************************************************************************************
33* Header Files *
34*********************************************************************************************************************************/
[35682]35#define LOG_GROUP LOG_GROUP_DEV_KBD
36#include <VBox/vmm/pdmdev.h>
[82167]37#ifndef IN_RING3
38# include <VBox/vmm/pdmapi.h>
39#endif
40#include <VBox/AssertGuest.h>
[48419]41#include <VBox/version.h>
[35682]42#include <iprt/assert.h>
43#include <iprt/uuid.h>
44
[44446]45/** @page pg_busmouse DevBusMouse - Microsoft Bus Mouse Emulation
46 *
47 * The Microsoft Bus Mouse was an early mouse sold by Microsoft, originally
[35682]48 * introduced in 1983. The mouse had a D-shaped 9-pin connector which plugged
49 * into a small ISA add-in board.
50 *
[44446]51 * The mouse itself was very simple (compared to a serial mouse) and most of the
52 * logic was located on the ISA board. Later, Microsoft sold an InPort mouse,
53 * which was also called a "bus mouse", but used a different interface.
54 *
[35682]55 * Microsoft part numbers for the Bus Mouse were 037-099 (100 ppi)
56 * and 037-199 (200 ppi).
57 *
58 * The Bus Mouse adapter included IRQ configuration jumpers (ref. MS article
59 * Q12230). The IRQ could be set to one of 2, 3, 4, 5. The typical setting
60 * would be IRQ 2 for a PC/XT and IRQ 5 for an AT compatible. Because IRQ 5
61 * may conflict with a SoundBlaster or a PCI device, this device defaults to
62 * IRQ 3. Note that IRQ 3 is also used by the COM 2 device, not often needed.
63 *
64 * The ISA adapter was built around an Intel 8255A compatible chip (ref.
65 * MS article Q46369). Once enabled, the adapter raises the configured IRQ
66 * 30 times per second; the rate is not configurable. The interrupts
67 * occur regardless of whether the mouse state has changed or not.
68 *
69 * To function properly, the 8255A must be programmed as follows:
70 * - Port A: Input. Used to read motion deltas and button states.
71 * - Port B: Output. Not used except for mouse detection.
72 * - Port C: Split. Upper bits set as output, used for control purposes.
73 * Lower bits set as input, reflecting IRQ state.
74 *
75 * Detailed information was gleaned from Windows and OS/2 DDK mouse samples.
76 */
77
[44446]78
[57358]79/*********************************************************************************************************************************
80* Defined Constants And Macros *
81*********************************************************************************************************************************/
[44446]82/** The original bus mouse controller is fixed at I/O port 0x23C. */
[35682]83#define BMS_IO_BASE 0x23C
84#define BMS_IO_SIZE 4
85
[44446]86/** @name Offsets relative to the I/O base.
87 *@{ */
88#define BMS_PORT_DATA 0 /**< 8255 Port A. */
89#define BMS_PORT_SIG 1 /**< 8255 Port B. */
90#define BMS_PORT_CTRL 2 /**< 8255 Port C. */
91#define BMS_PORT_INIT 3 /**< 8255 Control Port. */
92/** @} */
[35682]93
[44446]94/** @name Port C bits (control port).
95 * @{ */
96#define BMS_CTL_INT_DIS RT_BIT(4) /**< Disable IRQ (else enabled). */
97#define BMS_CTL_SEL_HIGH RT_BIT(5) /**< Select hi nibble (else lo). */
98#define BMS_CTL_SEL_Y RT_BIT(6) /**< Select X to read (else Y). */
99#define BMS_CTL_HOLD RT_BIT(7) /**< Hold counter (else clear). */
100/** @} */
[35682]101
[44446]102/** @name Port A bits (data port).
103 * @{ */
104#define BMS_DATA_DELTA 0x0F /**< Motion delta in lower nibble. */
105#define BMS_DATA_B3_UP RT_BIT(5) /**< Button 3 (right) is up. */
106#define BMS_DATA_B2_UP RT_BIT(6) /**< Button 2 (middle) is up. */
107#define BMS_DATA_B1_UP RT_BIT(7) /**< Button 1 (left) is up. */
108/** @} */
[35682]109
[44446]110/** Convert IRQ level (2/3/4/5) to a bit in the control register. */
[35682]111#define BMS_IRQ_BIT(a) (1 << (5 - a))
112
[44446]113/** IRQ period, corresponds to approx. 30 Hz. */
[35682]114#define BMS_IRQ_PERIOD_MS 34
115
[44446]116/** Default IRQ setting. */
[35682]117#define BMS_DEFAULT_IRQ 3
118
[44446]119/** The saved state version. */
[35682]120#define BMS_SAVED_STATE_VERSION 1
121
122
[57358]123/*********************************************************************************************************************************
124* Structures and Typedefs *
125*********************************************************************************************************************************/
[44514]126/**
[82167]127 * The shared Bus Mouse device state.
[44514]128 */
129typedef struct MouState
130{
[44626]131 /** @name 8255A state
132 * @{ */
[35682]133 uint8_t port_a;
134 uint8_t port_b;
135 uint8_t port_c;
136 uint8_t ctrl_port;
[44514]137 uint8_t cnt_held; /**< Counters held for reading. */
[35682]138 uint8_t held_dx;
139 uint8_t held_dy;
[44514]140 uint8_t irq; /**< The "jumpered" IRQ level. */
[35682]141 int32_t irq_toggle_counter;
142 /** Timer period in milliseconds. */
143 uint32_t cTimerPeriodMs;
[82167]144 /** Mouse timer handle. */
145 TMTIMERHANDLE hMouseTimer;
[44626]146 /** @} */
147
148 /** @name mouse state
149 * @{ */
[35682]150 int32_t disable_counter;
151 int32_t mouse_dx; /* current values, needed for 'poll' mode */
152 int32_t mouse_dy;
[82167]153 uint8_t mouse_enabled;
[35682]154 uint8_t mouse_buttons;
155 uint8_t mouse_buttons_reported;
[82167]156 uint8_t bAlignment;
[44626]157 /** @} */
[35682]158
[82167]159 /** The I/O ports registration. */
160 IOMIOPORTHANDLE hIoPorts;
[44446]161
[82167]162} MouState, BMSSTATE;
163/** Pointer to the shared Bus Mouse device state. */
164typedef BMSSTATE *PBMSSTATE;
165
166
167/**
168 * The ring-3 Bus Mouse device state.
169 */
170typedef struct BMSSTATER3
171{
172 /** Pointer to the device instance.
173 * @note Only for getting our bearings in an interface method. */
174 PPDMDEVINSR3 pDevIns;
175
[35682]176 /**
177 * Mouse port - LUN#0.
178 *
179 * @implements PDMIBASE
180 * @implements PDMIMOUSEPORT
181 */
182 struct
183 {
184 /** The base interface for the mouse port. */
185 PDMIBASE IBase;
186 /** The mouse port base interface. */
187 PDMIMOUSEPORT IPort;
188
189 /** The base interface of the attached mouse driver. */
190 R3PTRTYPE(PPDMIBASE) pDrvBase;
191 /** The mouse interface of the attached mouse driver. */
192 R3PTRTYPE(PPDMIMOUSECONNECTOR) pDrv;
193 } Mouse;
[82167]194} BMSSTATER3;
195/** Pointer to the ring-3 Bus Mouse device state. */
196typedef BMSSTATER3 *PBMSSTATER3;
[35682]197
[44446]198
[35682]199#ifndef VBOX_DEVICE_STRUCT_TESTCASE
200
[44446]201# ifdef IN_RING3
[35682]202
[44446]203/**
204 * Report a change in status down the driver chain.
205 *
206 * We want to report the mouse as enabled if and only if the guest is "using"
207 * it. That way, other devices (e.g. a PS/2 or USB mouse) can receive mouse
208 * events when the bus mouse is disabled. Enabling interrupts constitutes
209 * enabling the bus mouse. The mouse is considered disabled if interrupts are
210 * disabled for several consecutive mouse timer ticks; this is because the
211 * interrupt handler in the guest typically temporarily disables interrupts and
212 * we do not want to toggle the enabled/disabled state more often than
213 * necessary.
[35682]214 */
[82167]215static void bmsR3UpdateDownstreamStatus(PBMSSTATE pThis, PBMSSTATER3 pThisCC)
[35682]216{
[82167]217 PPDMIMOUSECONNECTOR pDrv = pThisCC->Mouse.pDrv;
[35682]218 bool fEnabled = !!pThis->mouse_enabled;
[94987]219 if (pDrv) /* pDrv may be NULL if no mouse interface attached. */
[95273]220 pDrv->pfnReportModes(pDrv, fEnabled, false, false, false);
[35682]221}
222
[44446]223/**
[82167]224 * Process a mouse event coming from the host.
[44446]225 */
[82167]226static void bmsR3MouseEvent(PBMSSTATE pThis, int dx, int dy, int dz, int dw, int buttons_state)
[35682]227{
228 LogRel3(("%s: dx=%d, dy=%d, dz=%d, dw=%d, buttons_state=0x%x\n",
229 __PRETTY_FUNCTION__, dx, dy, dz, dw, buttons_state));
230
231 /* Only record X/Y movement and buttons. */
[44446]232 pThis->mouse_dx += dx;
233 pThis->mouse_dy += dy;
234 pThis->mouse_buttons = buttons_state;
[35682]235}
236
[82167]237/**
238 * @callback_method_impl{FNTMTIMERDEV}
239 */
[87767]240static DECLCALLBACK(void) bmsR3TimerCallback(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
[35682]241{
[82167]242 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
[94987]243 PBMSSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PBMSSTATER3);
[35682]244 uint8_t irq_bit;
[94987]245 RT_NOREF(pvUser);
[87767]246 Assert(hTimer == pThis->hMouseTimer);
[35682]247
248 /* Toggle the IRQ line if interrupts are enabled. */
[44446]249 irq_bit = BMS_IRQ_BIT(pThis->irq);
[35682]250
[44446]251 if (pThis->port_c & irq_bit)
[35682]252 {
[44446]253 if (!(pThis->port_c & BMS_CTL_INT_DIS))
[82167]254 PDMDevHlpISASetIrq(pDevIns, pThis->irq, PDM_IRQ_LEVEL_LOW);
[44446]255 pThis->port_c &= ~irq_bit;
[35682]256 }
257 else
258 {
[44446]259 pThis->port_c |= irq_bit;
260 if (!(pThis->port_c & BMS_CTL_INT_DIS))
[82167]261 PDMDevHlpISASetIrq(pDevIns, pThis->irq, PDM_IRQ_LEVEL_HIGH);
[35682]262 }
263
264 /* Handle enabling/disabling of the mouse interface. */
[44446]265 if (pThis->port_c & BMS_CTL_INT_DIS)
[35682]266 {
[44446]267 if (pThis->disable_counter)
268 --pThis->disable_counter;
[35683]269
[44446]270 if (pThis->disable_counter == 0 && pThis->mouse_enabled)
[35682]271 {
[44446]272 pThis->mouse_enabled = false;
[82167]273 bmsR3UpdateDownstreamStatus(pThis, pThisCC);
[35682]274 }
275 }
276 else
277 {
[44446]278 pThis->disable_counter = 8; /* Re-arm the disable countdown. */
279 if (!pThis->mouse_enabled)
[35682]280 {
[44446]281 pThis->mouse_enabled = true;
[82167]282 bmsR3UpdateDownstreamStatus(pThis, pThisCC);
[35682]283 }
284 }
[44446]285
286 /* Re-arm the timer. */
[87767]287 PDMDevHlpTimerSetMillies(pDevIns, hTimer, pThis->cTimerPeriodMs);
[35682]288}
289
[44446]290# endif /* IN_RING3 */
[35682]291
[82167]292static void bmsSetReportedButtons(PBMSSTATE pThis, unsigned fButtons, unsigned fButtonMask)
[35682]293{
[44446]294 pThis->mouse_buttons_reported |= (fButtons & fButtonMask);
295 pThis->mouse_buttons_reported &= (fButtons | ~fButtonMask);
[35682]296}
297
[82167]298/**
299 * Update the internal state after a write to port C.
300 */
301static void bmsUpdateCtrl(PPDMDEVINS pDevIns, PBMSSTATE pThis)
[35682]302{
303 int32_t dx, dy;
304
305 /* If the controller is in hold state, transfer data from counters. */
[44446]306 if (pThis->port_c & BMS_CTL_HOLD)
[35682]307 {
[44446]308 if (!pThis->cnt_held)
[35682]309 {
[44446]310 pThis->cnt_held = true;
311 dx = pThis->mouse_dx < 0 ? RT_MAX(pThis->mouse_dx, -128)
312 : RT_MIN(pThis->mouse_dx, 127);
313 dy = pThis->mouse_dy < 0 ? RT_MAX(pThis->mouse_dy, -128)
314 : RT_MIN(pThis->mouse_dy, 127);
315 pThis->mouse_dx -= dx;
316 pThis->mouse_dy -= dy;
[82167]317 bmsSetReportedButtons(pThis, pThis->mouse_buttons & 0x07, 0x07);
[35682]318
319 /* Force type conversion. */
[44446]320 pThis->held_dx = dx;
321 pThis->held_dy = dy;
[35682]322 }
323 }
324 else
[44446]325 pThis->cnt_held = false;
[35682]326
327 /* Move the appropriate nibble into port A. */
[44446]328 if (pThis->cnt_held)
[35682]329 {
[44446]330 if (pThis->port_c & BMS_CTL_SEL_Y)
[35682]331 {
[44446]332 if (pThis->port_c & BMS_CTL_SEL_HIGH)
333 pThis->port_a = pThis->held_dy >> 4;
[35682]334 else
[44446]335 pThis->port_a = pThis->held_dy & 0xF;
[35682]336 }
337 else
338 {
[44446]339 if (pThis->port_c & BMS_CTL_SEL_HIGH)
340 pThis->port_a = pThis->held_dx >> 4;
[35682]341 else
[44446]342 pThis->port_a = pThis->held_dx & 0xF;
[35682]343 }
344 /* And update the button bits. */
[44446]345 pThis->port_a |= pThis->mouse_buttons & 1 ? 0 : BMS_DATA_B1_UP;
346 pThis->port_a |= pThis->mouse_buttons & 2 ? 0 : BMS_DATA_B3_UP;
347 pThis->port_a |= pThis->mouse_buttons & 4 ? 0 : BMS_DATA_B2_UP;
[35682]348 }
349 /* Immediately clear the IRQ if necessary. */
[44446]350 if (pThis->port_c & BMS_CTL_INT_DIS)
[35682]351 {
[82167]352 PDMDevHlpISASetIrq(pDevIns, pThis->irq, PDM_IRQ_LEVEL_LOW);
353 pThis->port_c &= ~BMS_IRQ_BIT(pThis->irq);
[35682]354 }
355}
356
357/**
[82167]358 * @callback_method_impl{FNIOMIOPORTNEWIN}
[35682]359 */
[82167]360static DECLCALLBACK(VBOXSTRICTRC) bmsIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
[35682]361{
[82167]362 RT_NOREF(pvUser);
[35682]363 if (cb == 1)
364 {
[82167]365 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
366 uint32_t uValue;
367
368 switch (offPort)
369 {
370 case BMS_PORT_DATA:
371 /* Read port A. */
372 uValue = pThis->port_a;
373 break;
374 case BMS_PORT_SIG:
375 /* Read port B. */
376 uValue = pThis->port_b;
377 break;
378 case BMS_PORT_CTRL:
379 /* Read port C. */
380 uValue = pThis->port_c;
381 /* Some Microsoft driver code reads the control port 10,000 times when
382 * determining the IRQ level. This can occur faster than the IRQ line
383 * transitions and the detection fails. To work around this, we force
384 * the IRQ bit to toggle every once in a while.
385 */
386 if (pThis->irq_toggle_counter)
387 pThis->irq_toggle_counter--;
388 else
389 {
390 pThis->irq_toggle_counter = 1000;
391 uValue ^= BMS_IRQ_BIT(pThis->irq);
392 }
393 break;
394 case BMS_PORT_INIT:
395 /* Read the 8255A control port. */
396 uValue = pThis->ctrl_port;
397 break;
398 default:
399 ASSERT_GUEST_MSG_FAILED(("invalid port %#x\n", offPort));
400 uValue = 0xff;
401 break;
402 }
403
404 *pu32 = uValue;
405 Log2(("mouIoPortRead: offPort=%#x+%x cb=%d *pu32=%#x\n", BMS_IO_BASE, offPort, cb, uValue));
406 LogRel3(("mouIoPortRead: read port %u: %#04x\n", offPort, uValue));
[44446]407 return VINF_SUCCESS;
[35682]408 }
[82167]409 ASSERT_GUEST_MSG_FAILED(("offPort=%#x cb=%d\n", offPort, cb));
[35682]410 return VERR_IOM_IOPORT_UNUSED;
411}
412
413/**
[82167]414 * @callback_method_impl{FNIOMIOPORTNEWOUT}
[35682]415 */
[82167]416static DECLCALLBACK(VBOXSTRICTRC) bmsIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
[35682]417{
[82167]418 RT_NOREF(pvUser);
[35682]419 if (cb == 1)
420 {
[82167]421 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
422 LogRel3(("mouIoPortWrite: write port %u: %#04x\n", offPort, u32));
423
424 switch (offPort)
425 {
426 case BMS_PORT_SIG:
427 /* Update port B. */
428 pThis->port_b = u32;
429 break;
430 case BMS_PORT_DATA:
431 /* Do nothing, port A is not writable. */
432 break;
433 case BMS_PORT_INIT:
434 pThis->ctrl_port = u32;
435 break;
436 case BMS_PORT_CTRL:
437 /* Update the high nibble of port C. */
438 pThis->port_c = (u32 & 0xF0) | (pThis->port_c & 0x0F);
439 bmsUpdateCtrl(pDevIns, pThis);
440 break;
441 default:
442 ASSERT_GUEST_MSG_FAILED(("invalid port %#x\n", offPort));
443 break;
444 }
445
[82168]446 Log2(("mouIoPortWrite: offPort=%#x+%u cb=%d u32=%#x\n", BMS_IO_BASE, offPort, cb, u32));
[35682]447 }
448 else
[82167]449 ASSERT_GUEST_MSG_FAILED(("offPort=%#x cb=%d\n", offPort, cb));
450 return VINF_SUCCESS;
[35682]451}
452
[44446]453# ifdef IN_RING3
[35682]454
455/**
[82167]456 * @callback_method_impl{FNSSMDEVSAVEEXEC}
[35682]457 */
[82167]458static DECLCALLBACK(int) bmsR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
[35682]459{
[82167]460 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
461 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
[35682]462
463 /* 8255A state. */
[82167]464 pHlp->pfnSSMPutU8(pSSMHandle, pThis->port_a);
465 pHlp->pfnSSMPutU8(pSSMHandle, pThis->port_b);
466 pHlp->pfnSSMPutU8(pSSMHandle, pThis->port_c);
467 pHlp->pfnSSMPutU8(pSSMHandle, pThis->ctrl_port);
[35682]468 /* Other device state. */
[82167]469 pHlp->pfnSSMPutU8(pSSMHandle, pThis->cnt_held);
470 pHlp->pfnSSMPutU8(pSSMHandle, pThis->held_dx);
471 pHlp->pfnSSMPutU8(pSSMHandle, pThis->held_dy);
472 pHlp->pfnSSMPutU8(pSSMHandle, pThis->irq);
473 pHlp->pfnSSMPutU32(pSSMHandle, pThis->cTimerPeriodMs);
[35682]474 /* Current mouse state deltas. */
[82167]475 pHlp->pfnSSMPutS32(pSSMHandle, pThis->mouse_dx);
476 pHlp->pfnSSMPutS32(pSSMHandle, pThis->mouse_dy);
477 pHlp->pfnSSMPutU8(pSSMHandle, pThis->mouse_buttons_reported);
[35682]478 /* Timer. */
[82167]479 return PDMDevHlpTimerSave(pDevIns, pThis->hMouseTimer, pSSMHandle);
[35682]480}
481
482/**
[82167]483 * @callback_method_impl{FNSSMDEVLOADEXEC}
[35682]484 */
[82167]485static DECLCALLBACK(int) bmsR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t uVersion, uint32_t uPass)
[35682]486{
[82167]487 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
488 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
[35682]489
490 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
491
492 if (uVersion > BMS_SAVED_STATE_VERSION)
493 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
494
495 /* 8255A state. */
[82167]496 pHlp->pfnSSMGetU8(pSSMHandle, &pThis->port_a);
497 pHlp->pfnSSMGetU8(pSSMHandle, &pThis->port_b);
498 pHlp->pfnSSMGetU8(pSSMHandle, &pThis->port_c);
499 pHlp->pfnSSMGetU8(pSSMHandle, &pThis->ctrl_port);
[35682]500 /* Other device state. */
[82167]501 pHlp->pfnSSMGetU8(pSSMHandle, &pThis->cnt_held);
502 pHlp->pfnSSMGetU8(pSSMHandle, &pThis->held_dx);
503 pHlp->pfnSSMGetU8(pSSMHandle, &pThis->held_dy);
504 pHlp->pfnSSMGetU8(pSSMHandle, &pThis->irq);
505 pHlp->pfnSSMGetU32(pSSMHandle, &pThis->cTimerPeriodMs);
[35682]506 /* Current mouse state deltas. */
[82167]507 pHlp->pfnSSMGetS32(pSSMHandle, &pThis->mouse_dx);
508 pHlp->pfnSSMGetS32(pSSMHandle, &pThis->mouse_dy);
509 pHlp->pfnSSMGetU8(pSSMHandle, &pThis->mouse_buttons_reported);
[35682]510 /* Timer. */
[82167]511 return PDMDevHlpTimerLoad(pDevIns, pThis->hMouseTimer, pSSMHandle);
[35682]512}
513
514/**
515 * Reset notification.
516 *
[58170]517 * @returns VBox status code.
[35682]518 * @param pDevIns The device instance data.
519 */
[82167]520static DECLCALLBACK(void) bmsR3Reset(PPDMDEVINS pDevIns)
[35682]521{
[82167]522 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
[94987]523 PBMSSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PBMSSTATER3);
[35682]524
525 /* Reinitialize the timer. */
526 pThis->cTimerPeriodMs = BMS_IRQ_PERIOD_MS / 2;
[82167]527 PDMDevHlpTimerSetMillies(pDevIns, pThis->hMouseTimer, pThis->cTimerPeriodMs);
[35682]528
[82167]529 /* Clear the device setup. */
530 pThis->port_a = pThis->port_b = 0;
531 pThis->port_c = BMS_CTL_INT_DIS; /* Interrupts disabled. */
532 pThis->ctrl_port = 0x91; /* Default 8255A setup. */
533
534 /* Clear motion/button state. */
535 pThis->cnt_held = false;
536 pThis->mouse_dx = pThis->mouse_dy = 0;
537 pThis->mouse_buttons = 0;
538 pThis->mouse_buttons_reported = 0;
539 pThis->disable_counter = 0;
540 pThis->irq_toggle_counter = 1000;
541
542 if (pThis->mouse_enabled)
543 {
544 pThis->mouse_enabled = false;
545 bmsR3UpdateDownstreamStatus(pThis, pThisCC);
546 }
[35682]547}
548
549
550/* -=-=-=-=-=- Mouse: IBase -=-=-=-=-=- */
551
552/**
553 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
554 */
[82167]555static DECLCALLBACK(void *) bmsR3Base_QueryMouseInterface(PPDMIBASE pInterface, const char *pszIID)
[35682]556{
[82167]557 PBMSSTATER3 pThisCC = RT_FROM_MEMBER(pInterface, BMSSTATER3, Mouse.IBase);
558 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->Mouse.IBase);
559 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUSEPORT, &pThisCC->Mouse.IPort);
[35682]560 return NULL;
561}
562
563
564/* -=-=-=-=-=- Mouse: IMousePort -=-=-=-=-=- */
565
566/**
[65104]567 * @interface_method_impl{PDMIMOUSEPORT,pfnPutEvent}
[35682]568 */
[82167]569static DECLCALLBACK(int) bmsR3MousePort_PutEvent(PPDMIMOUSEPORT pInterface, int32_t dx,
570 int32_t dy, int32_t dz, int32_t dw,
571 uint32_t fButtons)
[35682]572{
[82167]573 PBMSSTATER3 pThisCC = RT_FROM_MEMBER(pInterface, BMSSTATER3, Mouse.IPort);
574 PPDMDEVINS pDevIns = pThisCC->pDevIns;
575 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
576 int rc = PDMDevHlpCritSectEnter(pDevIns, pDevIns->CTX_SUFF(pCritSectRo), VERR_SEM_BUSY);
[90447]577 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->CTX_SUFF(pCritSectRo), rc);
[35682]578
[82167]579 bmsR3MouseEvent(pThis, dx, dy, dz, dw, fButtons);
[35682]580
[82167]581 PDMDevHlpCritSectLeave(pDevIns, pDevIns->CTX_SUFF(pCritSectRo));
[35682]582 return VINF_SUCCESS;
583}
584
585/**
[65104]586 * @interface_method_impl{PDMIMOUSEPORT,pfnPutEventAbs}
[35682]587 */
[82167]588static DECLCALLBACK(int) bmsR3MousePort_PutEventAbs(PPDMIMOUSEPORT pInterface, uint32_t x, uint32_t y,
589 int32_t dz, int32_t dw, uint32_t fButtons)
[35682]590{
[63419]591 RT_NOREF(pInterface, x, y, dz, dw, fButtons);
[35682]592 AssertFailedReturn(VERR_NOT_SUPPORTED);
593}
594
[47208]595/**
[65104]596 * @interface_method_impl{PDMIMOUSEPORT,pfnPutEventMultiTouch}
[47208]597 */
[82167]598static DECLCALLBACK(int) bmsR3MousePort_PutEventMultiTouch(PPDMIMOUSEPORT pInterface, uint8_t cContacts,
599 const uint64_t *pau64Contacts, uint32_t u32ScanTime)
[47208]600{
[63419]601 RT_NOREF(pInterface, cContacts, pau64Contacts, u32ScanTime);
[47208]602 AssertFailedReturn(VERR_NOT_SUPPORTED);
603}
604
[35682]605/* -=-=-=-=-=- setup code -=-=-=-=-=- */
606
607
608/**
[82167]609 * @interface_method_impl{PDMDEVREGR3,pfnAttach}
[35682]610 */
[82167]611static DECLCALLBACK(int) bmsR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
[35682]612{
[82167]613 PBMSSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PBMSSTATER3);
[35682]614 int rc;
615
616 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
617 ("Bus mouse device does not support hotplugging\n"),
618 VERR_INVALID_PARAMETER);
619
620 switch (iLUN)
621 {
622 /* LUN #0: mouse */
623 case 0:
[82167]624 rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThisCC->Mouse.IBase, &pThisCC->Mouse.pDrvBase, "Bus Mouse Port");
[35682]625 if (RT_SUCCESS(rc))
626 {
[82167]627 pThisCC->Mouse.pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->Mouse.pDrvBase, PDMIMOUSECONNECTOR);
628 if (!pThisCC->Mouse.pDrv)
[35682]629 {
630 AssertLogRelMsgFailed(("LUN #0 doesn't have a mouse interface! rc=%Rrc\n", rc));
631 rc = VERR_PDM_MISSING_INTERFACE;
632 }
633 }
634 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
635 {
[94987]636 LogRel(("%s/%d: Warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
[35682]637 rc = VINF_SUCCESS;
638 }
639 else
640 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
641 break;
642
643 default:
644 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
645 return VERR_PDM_NO_SUCH_LUN;
646 }
647
648 return rc;
649}
650
651
652/**
[82167]653 * @interface_method_impl{PDMDEVREGR3,pfnDetach}
[35682]654 */
[82167]655static DECLCALLBACK(void) bmsR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
[35682]656{
[82167]657# if 0
[35682]658 /*
659 * Reset the interfaces and update the controller state.
660 */
[82167]661 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
[35682]662 switch (iLUN)
663 {
664 /* LUN #0: mouse */
665 case 0:
666 pThis->Mouse.pDrv = NULL;
667 pThis->Mouse.pDrvBase = NULL;
668 break;
669
670 default:
671 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
672 break;
673 }
[82167]674# else
[63419]675 RT_NOREF(pDevIns, iLUN, fFlags);
[82167]676# endif
[35682]677}
678
679
680/**
681 * @interface_method_impl{PDMDEVREG,pfnConstruct}
682 */
[82167]683static DECLCALLBACK(int) bmsR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
[35682]684{
[82167]685 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
686 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
687 PBMSSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PBMSSTATER3);
688 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
689 int rc;
[63419]690 RT_NOREF(iInstance);
[82167]691
[35682]692 Assert(iInstance == 0);
693
694 /*
695 * Validate and read the configuration.
696 */
[82167]697 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "IRQ", "");
698
699 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IRQ", &pThis->irq, BMS_DEFAULT_IRQ);
[35682]700 if (RT_FAILURE(rc))
701 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"IRQ\" from the config"));
[82167]702 if (pThis->irq < 2 || pThis->irq > 5)
[35682]703 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Invalid \"IRQ\" config setting"));
[35683]704
[82167]705 Log(("busmouse: IRQ=%u fRCEnabled=%RTbool fR0Enabled=%RTbool\n", pThis->irq, pDevIns->fRCEnabled, pDevIns->fR0Enabled));
[35682]706
707 /*
708 * Initialize the interfaces.
709 */
[82167]710 pThisCC->pDevIns = pDevIns;
711 pThisCC->Mouse.IBase.pfnQueryInterface = bmsR3Base_QueryMouseInterface;
712 pThisCC->Mouse.IPort.pfnPutEvent = bmsR3MousePort_PutEvent;
713 pThisCC->Mouse.IPort.pfnPutEventAbs = bmsR3MousePort_PutEventAbs;
[95273]714 pThisCC->Mouse.IPort.pfnPutEventTouchScreen = bmsR3MousePort_PutEventMultiTouch;
715 pThisCC->Mouse.IPort.pfnPutEventTouchPad = bmsR3MousePort_PutEventMultiTouch;
[35682]716
717 /*
718 * Create the interrupt timer.
719 */
[82167]720 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, bmsR3TimerCallback, pThis,
[87773]721 TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "Bus Mouse", &pThis->hMouseTimer);
[82167]722 AssertRCReturn(rc, rc);
[35682]723
724 /*
[82167]725 * Register I/O ports.
[35682]726 */
[82167]727 static const IOMIOPORTDESC s_aDescs[] =
[35682]728 {
[82167]729 { "DATA", "DATA", NULL, NULL },
730 { "SIG", "SIG", NULL, NULL },
731 { "CTRL", "CTRL", NULL, NULL },
732 { "INIT", "INIT", NULL, NULL },
733 { NULL, NULL, NULL, NULL }
734 };
735 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, BMS_IO_BASE, BMS_IO_SIZE, bmsIoPortWrite, bmsIoPortRead,
736 "Bus Mouse", s_aDescs, &pThis->hIoPorts);
737 AssertRCReturn(rc, rc);
[35682]738
739 /*
[82167]740 * Register saved state.
741 */
742 rc = PDMDevHlpSSMRegister(pDevIns, BMS_SAVED_STATE_VERSION, sizeof(*pThis), bmsR3SaveExec, bmsR3LoadExec);
743 AssertRCReturn(rc, rc);
744
745 /*
[35682]746 * Attach to the mouse driver.
747 */
[82167]748 rc = bmsR3Attach(pDevIns, 0, PDM_TACH_FLAGS_NOT_HOT_PLUG);
749 AssertRCReturn(rc, rc);
[35682]750
751 /*
752 * Initialize the device state.
753 */
[82167]754 bmsR3Reset(pDevIns);
[35682]755
756 return VINF_SUCCESS;
757}
758
[82167]759# else /* !IN_RING3 */
[35682]760
[82167]761/**
762 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
763 */
764static DECLCALLBACK(int) bmsRZConstruct(PPDMDEVINS pDevIns)
765{
766 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
767 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
[80531]768
[82167]769 int rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPorts, bmsIoPortWrite, bmsIoPortRead, NULL /*pvUser*/);
770 AssertRCReturn(rc, rc);
771
772 return VINF_SUCCESS;
773}
774
775# endif /* !IN_RING3 */
776
777
[35682]778/**
779 * The device registration structure.
780 */
[82167]781static const PDMDEVREG g_DeviceBusMouse =
[35682]782{
[80531]783 /* .u32Version = */ PDM_DEVREG_VERSION,
784 /* .uReserved0 = */ 0,
785 /* .szName = */ "busmouse",
[82167]786 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS /** @todo | PDM_DEVREG_FLAGS_RZ */ | PDM_DEVREG_FLAGS_NEW_STYLE,
[80531]787 /* .fClass = */ PDM_DEVREG_CLASS_INPUT,
788 /* .cMaxInstances = */ 1,
789 /* .uSharedVersion = */ 42,
[82167]790 /* .cbInstanceShared = */ sizeof(BMSSTATE),
791 /* .cbInstanceCC = */ CTX_EXPR(sizeof(BMSSTATER3), 0, 0),
[80531]792 /* .cbInstanceRC = */ 0,
[80703]793 /* .cMaxPciDevices = */ 0,
[80704]794 /* .cMaxMsixVectors = */ 0,
[80531]795 /* .pszDescription = */ "Microsoft Bus Mouse controller. LUN #0 is the mouse connector.",
796# if defined(IN_RING3)
797 /* .pszRCMod = */ "VBoxDDRC.rc",
798 /* .pszR0Mod = */ "VBoxDDR0.r0",
[82167]799 /* .pfnConstruct = */ bmsR3Construct,
[80531]800 /* .pfnDestruct = */ NULL,
[82167]801 /* .pfnRelocate = */ NULL,
[80531]802 /* .pfnMemSetup = */ NULL,
803 /* .pfnPowerOn = */ NULL,
[82167]804 /* .pfnReset = */ bmsR3Reset,
[80531]805 /* .pfnSuspend = */ NULL,
806 /* .pfnResume = */ NULL,
[82167]807 /* .pfnAttach = */ bmsR3Attach,
808 /* .pfnDetach = */ bmsR3Detach,
[80531]809 /* .pfnQueryInterface = */ NULL,
810 /* .pfnInitComplete = */ NULL,
811 /* .pfnPowerOff = */ NULL,
812 /* .pfnSoftReset = */ NULL,
813 /* .pfnReserved0 = */ NULL,
814 /* .pfnReserved1 = */ NULL,
815 /* .pfnReserved2 = */ NULL,
816 /* .pfnReserved3 = */ NULL,
817 /* .pfnReserved4 = */ NULL,
818 /* .pfnReserved5 = */ NULL,
819 /* .pfnReserved6 = */ NULL,
820 /* .pfnReserved7 = */ NULL,
821# elif defined(IN_RING0)
822 /* .pfnEarlyConstruct = */ NULL,
[82168]823 /* .pfnConstruct = */ bmsRZConstruct,
[80531]824 /* .pfnDestruct = */ NULL,
825 /* .pfnFinalDestruct = */ NULL,
826 /* .pfnRequest = */ NULL,
827 /* .pfnReserved0 = */ NULL,
828 /* .pfnReserved1 = */ NULL,
829 /* .pfnReserved2 = */ NULL,
830 /* .pfnReserved3 = */ NULL,
831 /* .pfnReserved4 = */ NULL,
832 /* .pfnReserved5 = */ NULL,
833 /* .pfnReserved6 = */ NULL,
834 /* .pfnReserved7 = */ NULL,
835# elif defined(IN_RC)
[82168]836 /* .pfnConstruct = */ bmsRZConstruct,
[80531]837 /* .pfnReserved0 = */ NULL,
838 /* .pfnReserved1 = */ NULL,
839 /* .pfnReserved2 = */ NULL,
840 /* .pfnReserved3 = */ NULL,
841 /* .pfnReserved4 = */ NULL,
842 /* .pfnReserved5 = */ NULL,
843 /* .pfnReserved6 = */ NULL,
844 /* .pfnReserved7 = */ NULL,
845# else
846# error "Not in IN_RING3, IN_RING0 or IN_RC!"
847# endif
848 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
[35682]849};
850
[82167]851# ifdef VBOX_IN_EXTPACK_R3
852
[48419]853/**
854 * @callback_method_impl{FNPDMVBOXDEVICESREGISTER}
855 */
856extern "C" DECLEXPORT(int) VBoxDevicesRegister(PPDMDEVREGCB pCallbacks, uint32_t u32Version)
857{
858 AssertLogRelMsgReturn(u32Version >= VBOX_VERSION,
859 ("u32Version=%#x VBOX_VERSION=%#x\n", u32Version, VBOX_VERSION),
860 VERR_EXTPACK_VBOX_VERSION_MISMATCH);
861 AssertLogRelMsgReturn(pCallbacks->u32Version == PDM_DEVREG_CB_VERSION,
862 ("pCallbacks->u32Version=%#x PDM_DEVREG_CB_VERSION=%#x\n", pCallbacks->u32Version, PDM_DEVREG_CB_VERSION),
863 VERR_VERSION_MISMATCH);
864
865 return pCallbacks->pfnRegister(pCallbacks, &g_DeviceBusMouse);
866}
867
[82167]868# else /* !VBOX_IN_EXTPACK_R3 */
869
870/** Pointer to the ring-0 device registrations for the Bus Mouse. */
871static PCPDMDEVREGR0 g_apDevRegs[] =
872{
873 &g_DeviceBusMouse,
874};
875
876/** Module device registration record for the Bus Mouse. */
877static PDMDEVMODREGR0 g_ModDevReg =
878{
879 /* .u32Version = */ PDM_DEVMODREGR0_VERSION,
880 /* .cDevRegs = */ RT_ELEMENTS(g_apDevRegs),
881 /* .papDevRegs = */ &g_apDevRegs[0],
882 /* .hMod = */ NULL,
883 /* .ListEntry = */ { NULL, NULL },
884};
885
886DECLEXPORT(int) ModuleInit(void *hMod)
887{
888 LogFlow(("VBoxBusMouseRZ/ModuleInit: %p\n", hMod));
889 return PDMR0DeviceRegisterModule(hMod, &g_ModDevReg);
890}
891
892DECLEXPORT(void) ModuleTerm(void *hMod)
893{
894 LogFlow(("VBoxBusMouseRZ/ModuleTerm: %p\n", hMod));
895 PDMR0DeviceDeregisterModule(hMod, &g_ModDevReg);
896}
897
898# endif /* !VBOX_IN_EXTPACK_R3 */
899
[35682]900#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
[35683]901
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use