VirtualBox

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

Last change on this file since 94967 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.9 KB
Line 
1/* $Id: DevBusMouse.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * BusMouse - Microsoft Bus (parallel) mouse controller device.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
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
24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
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.
29 */
30
31
32/*********************************************************************************************************************************
33* Header Files *
34*********************************************************************************************************************************/
35#define LOG_GROUP LOG_GROUP_DEV_KBD
36#include <VBox/vmm/pdmdev.h>
37#ifndef IN_RING3
38# include <VBox/vmm/pdmapi.h>
39#endif
40#include <VBox/AssertGuest.h>
41#include <VBox/version.h>
42#include <iprt/assert.h>
43#include <iprt/uuid.h>
44
45/** @page pg_busmouse DevBusMouse - Microsoft Bus Mouse Emulation
46 *
47 * The Microsoft Bus Mouse was an early mouse sold by Microsoft, originally
48 * introduced in 1983. The mouse had a D-shaped 9-pin connector which plugged
49 * into a small ISA add-in board.
50 *
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 *
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
78
79/*********************************************************************************************************************************
80* Defined Constants And Macros *
81*********************************************************************************************************************************/
82/** The original bus mouse controller is fixed at I/O port 0x23C. */
83#define BMS_IO_BASE 0x23C
84#define BMS_IO_SIZE 4
85
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/** @} */
93
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/** @} */
101
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/** @} */
109
110/** Convert IRQ level (2/3/4/5) to a bit in the control register. */
111#define BMS_IRQ_BIT(a) (1 << (5 - a))
112
113/** IRQ period, corresponds to approx. 30 Hz. */
114#define BMS_IRQ_PERIOD_MS 34
115
116/** Default IRQ setting. */
117#define BMS_DEFAULT_IRQ 3
118
119/** The saved state version. */
120#define BMS_SAVED_STATE_VERSION 1
121
122
123/*********************************************************************************************************************************
124* Structures and Typedefs *
125*********************************************************************************************************************************/
126/**
127 * The shared Bus Mouse device state.
128 */
129typedef struct MouState
130{
131 /** @name 8255A state
132 * @{ */
133 uint8_t port_a;
134 uint8_t port_b;
135 uint8_t port_c;
136 uint8_t ctrl_port;
137 uint8_t cnt_held; /**< Counters held for reading. */
138 uint8_t held_dx;
139 uint8_t held_dy;
140 uint8_t irq; /**< The "jumpered" IRQ level. */
141 int32_t irq_toggle_counter;
142 /** Timer period in milliseconds. */
143 uint32_t cTimerPeriodMs;
144 /** Mouse timer handle. */
145 TMTIMERHANDLE hMouseTimer;
146 /** @} */
147
148 /** @name mouse state
149 * @{ */
150 int32_t disable_counter;
151 int32_t mouse_dx; /* current values, needed for 'poll' mode */
152 int32_t mouse_dy;
153 uint8_t mouse_enabled;
154 uint8_t mouse_buttons;
155 uint8_t mouse_buttons_reported;
156 uint8_t bAlignment;
157 /** @} */
158
159 /** The I/O ports registration. */
160 IOMIOPORTHANDLE hIoPorts;
161
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
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;
194} BMSSTATER3;
195/** Pointer to the ring-3 Bus Mouse device state. */
196typedef BMSSTATER3 *PBMSSTATER3;
197
198
199#ifndef VBOX_DEVICE_STRUCT_TESTCASE
200
201# ifdef IN_RING3
202
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.
214 */
215static void bmsR3UpdateDownstreamStatus(PBMSSTATE pThis, PBMSSTATER3 pThisCC)
216{
217 PPDMIMOUSECONNECTOR pDrv = pThisCC->Mouse.pDrv;
218 bool fEnabled = !!pThis->mouse_enabled;
219 pDrv->pfnReportModes(pDrv, fEnabled, false, false);
220}
221
222/**
223 * Process a mouse event coming from the host.
224 */
225static void bmsR3MouseEvent(PBMSSTATE pThis, int dx, int dy, int dz, int dw, int buttons_state)
226{
227 LogRel3(("%s: dx=%d, dy=%d, dz=%d, dw=%d, buttons_state=0x%x\n",
228 __PRETTY_FUNCTION__, dx, dy, dz, dw, buttons_state));
229
230 /* Only record X/Y movement and buttons. */
231 pThis->mouse_dx += dx;
232 pThis->mouse_dy += dy;
233 pThis->mouse_buttons = buttons_state;
234}
235
236/**
237 * @callback_method_impl{FNTMTIMERDEV}
238 */
239static DECLCALLBACK(void) bmsR3TimerCallback(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
240{
241 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
242 PBMSSTATER3 pThisCC = PDMDEVINS_2_DATA(pDevIns, PBMSSTATER3);
243 uint8_t irq_bit;
244 RT_NOREF(pvUser, hTimer);
245 Assert(hTimer == pThis->hMouseTimer);
246
247 /* Toggle the IRQ line if interrupts are enabled. */
248 irq_bit = BMS_IRQ_BIT(pThis->irq);
249
250 if (pThis->port_c & irq_bit)
251 {
252 if (!(pThis->port_c & BMS_CTL_INT_DIS))
253 PDMDevHlpISASetIrq(pDevIns, pThis->irq, PDM_IRQ_LEVEL_LOW);
254 pThis->port_c &= ~irq_bit;
255 }
256 else
257 {
258 pThis->port_c |= irq_bit;
259 if (!(pThis->port_c & BMS_CTL_INT_DIS))
260 PDMDevHlpISASetIrq(pDevIns, pThis->irq, PDM_IRQ_LEVEL_HIGH);
261 }
262
263 /* Handle enabling/disabling of the mouse interface. */
264 if (pThis->port_c & BMS_CTL_INT_DIS)
265 {
266 if (pThis->disable_counter)
267 --pThis->disable_counter;
268
269 if (pThis->disable_counter == 0 && pThis->mouse_enabled)
270 {
271 pThis->mouse_enabled = false;
272 bmsR3UpdateDownstreamStatus(pThis, pThisCC);
273 }
274 }
275 else
276 {
277 pThis->disable_counter = 8; /* Re-arm the disable countdown. */
278 if (!pThis->mouse_enabled)
279 {
280 pThis->mouse_enabled = true;
281 bmsR3UpdateDownstreamStatus(pThis, pThisCC);
282 }
283 }
284
285 /* Re-arm the timer. */
286 PDMDevHlpTimerSetMillies(pDevIns, hTimer, pThis->cTimerPeriodMs);
287}
288
289# endif /* IN_RING3 */
290
291static void bmsSetReportedButtons(PBMSSTATE pThis, unsigned fButtons, unsigned fButtonMask)
292{
293 pThis->mouse_buttons_reported |= (fButtons & fButtonMask);
294 pThis->mouse_buttons_reported &= (fButtons | ~fButtonMask);
295}
296
297/**
298 * Update the internal state after a write to port C.
299 */
300static void bmsUpdateCtrl(PPDMDEVINS pDevIns, PBMSSTATE pThis)
301{
302 int32_t dx, dy;
303
304 /* If the controller is in hold state, transfer data from counters. */
305 if (pThis->port_c & BMS_CTL_HOLD)
306 {
307 if (!pThis->cnt_held)
308 {
309 pThis->cnt_held = true;
310 dx = pThis->mouse_dx < 0 ? RT_MAX(pThis->mouse_dx, -128)
311 : RT_MIN(pThis->mouse_dx, 127);
312 dy = pThis->mouse_dy < 0 ? RT_MAX(pThis->mouse_dy, -128)
313 : RT_MIN(pThis->mouse_dy, 127);
314 pThis->mouse_dx -= dx;
315 pThis->mouse_dy -= dy;
316 bmsSetReportedButtons(pThis, pThis->mouse_buttons & 0x07, 0x07);
317
318 /* Force type conversion. */
319 pThis->held_dx = dx;
320 pThis->held_dy = dy;
321 }
322 }
323 else
324 pThis->cnt_held = false;
325
326 /* Move the appropriate nibble into port A. */
327 if (pThis->cnt_held)
328 {
329 if (pThis->port_c & BMS_CTL_SEL_Y)
330 {
331 if (pThis->port_c & BMS_CTL_SEL_HIGH)
332 pThis->port_a = pThis->held_dy >> 4;
333 else
334 pThis->port_a = pThis->held_dy & 0xF;
335 }
336 else
337 {
338 if (pThis->port_c & BMS_CTL_SEL_HIGH)
339 pThis->port_a = pThis->held_dx >> 4;
340 else
341 pThis->port_a = pThis->held_dx & 0xF;
342 }
343 /* And update the button bits. */
344 pThis->port_a |= pThis->mouse_buttons & 1 ? 0 : BMS_DATA_B1_UP;
345 pThis->port_a |= pThis->mouse_buttons & 2 ? 0 : BMS_DATA_B3_UP;
346 pThis->port_a |= pThis->mouse_buttons & 4 ? 0 : BMS_DATA_B2_UP;
347 }
348 /* Immediately clear the IRQ if necessary. */
349 if (pThis->port_c & BMS_CTL_INT_DIS)
350 {
351 PDMDevHlpISASetIrq(pDevIns, pThis->irq, PDM_IRQ_LEVEL_LOW);
352 pThis->port_c &= ~BMS_IRQ_BIT(pThis->irq);
353 }
354}
355
356/**
357 * @callback_method_impl{FNIOMIOPORTNEWIN}
358 */
359static DECLCALLBACK(VBOXSTRICTRC) bmsIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
360{
361 RT_NOREF(pvUser);
362 if (cb == 1)
363 {
364 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
365 uint32_t uValue;
366
367 switch (offPort)
368 {
369 case BMS_PORT_DATA:
370 /* Read port A. */
371 uValue = pThis->port_a;
372 break;
373 case BMS_PORT_SIG:
374 /* Read port B. */
375 uValue = pThis->port_b;
376 break;
377 case BMS_PORT_CTRL:
378 /* Read port C. */
379 uValue = pThis->port_c;
380 /* Some Microsoft driver code reads the control port 10,000 times when
381 * determining the IRQ level. This can occur faster than the IRQ line
382 * transitions and the detection fails. To work around this, we force
383 * the IRQ bit to toggle every once in a while.
384 */
385 if (pThis->irq_toggle_counter)
386 pThis->irq_toggle_counter--;
387 else
388 {
389 pThis->irq_toggle_counter = 1000;
390 uValue ^= BMS_IRQ_BIT(pThis->irq);
391 }
392 break;
393 case BMS_PORT_INIT:
394 /* Read the 8255A control port. */
395 uValue = pThis->ctrl_port;
396 break;
397 default:
398 ASSERT_GUEST_MSG_FAILED(("invalid port %#x\n", offPort));
399 uValue = 0xff;
400 break;
401 }
402
403 *pu32 = uValue;
404 Log2(("mouIoPortRead: offPort=%#x+%x cb=%d *pu32=%#x\n", BMS_IO_BASE, offPort, cb, uValue));
405 LogRel3(("mouIoPortRead: read port %u: %#04x\n", offPort, uValue));
406 return VINF_SUCCESS;
407 }
408 ASSERT_GUEST_MSG_FAILED(("offPort=%#x cb=%d\n", offPort, cb));
409 return VERR_IOM_IOPORT_UNUSED;
410}
411
412/**
413 * @callback_method_impl{FNIOMIOPORTNEWOUT}
414 */
415static DECLCALLBACK(VBOXSTRICTRC) bmsIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
416{
417 RT_NOREF(pvUser);
418 if (cb == 1)
419 {
420 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
421 LogRel3(("mouIoPortWrite: write port %u: %#04x\n", offPort, u32));
422
423 switch (offPort)
424 {
425 case BMS_PORT_SIG:
426 /* Update port B. */
427 pThis->port_b = u32;
428 break;
429 case BMS_PORT_DATA:
430 /* Do nothing, port A is not writable. */
431 break;
432 case BMS_PORT_INIT:
433 pThis->ctrl_port = u32;
434 break;
435 case BMS_PORT_CTRL:
436 /* Update the high nibble of port C. */
437 pThis->port_c = (u32 & 0xF0) | (pThis->port_c & 0x0F);
438 bmsUpdateCtrl(pDevIns, pThis);
439 break;
440 default:
441 ASSERT_GUEST_MSG_FAILED(("invalid port %#x\n", offPort));
442 break;
443 }
444
445 Log2(("mouIoPortWrite: offPort=%#x+%u cb=%d u32=%#x\n", BMS_IO_BASE, offPort, cb, u32));
446 }
447 else
448 ASSERT_GUEST_MSG_FAILED(("offPort=%#x cb=%d\n", offPort, cb));
449 return VINF_SUCCESS;
450}
451
452# ifdef IN_RING3
453
454/**
455 * @callback_method_impl{FNSSMDEVSAVEEXEC}
456 */
457static DECLCALLBACK(int) bmsR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
458{
459 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
460 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
461
462 /* 8255A state. */
463 pHlp->pfnSSMPutU8(pSSMHandle, pThis->port_a);
464 pHlp->pfnSSMPutU8(pSSMHandle, pThis->port_b);
465 pHlp->pfnSSMPutU8(pSSMHandle, pThis->port_c);
466 pHlp->pfnSSMPutU8(pSSMHandle, pThis->ctrl_port);
467 /* Other device state. */
468 pHlp->pfnSSMPutU8(pSSMHandle, pThis->cnt_held);
469 pHlp->pfnSSMPutU8(pSSMHandle, pThis->held_dx);
470 pHlp->pfnSSMPutU8(pSSMHandle, pThis->held_dy);
471 pHlp->pfnSSMPutU8(pSSMHandle, pThis->irq);
472 pHlp->pfnSSMPutU32(pSSMHandle, pThis->cTimerPeriodMs);
473 /* Current mouse state deltas. */
474 pHlp->pfnSSMPutS32(pSSMHandle, pThis->mouse_dx);
475 pHlp->pfnSSMPutS32(pSSMHandle, pThis->mouse_dy);
476 pHlp->pfnSSMPutU8(pSSMHandle, pThis->mouse_buttons_reported);
477 /* Timer. */
478 return PDMDevHlpTimerSave(pDevIns, pThis->hMouseTimer, pSSMHandle);
479}
480
481/**
482 * @callback_method_impl{FNSSMDEVLOADEXEC}
483 */
484static DECLCALLBACK(int) bmsR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t uVersion, uint32_t uPass)
485{
486 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
487 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
488
489 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
490
491 if (uVersion > BMS_SAVED_STATE_VERSION)
492 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
493
494 /* 8255A state. */
495 pHlp->pfnSSMGetU8(pSSMHandle, &pThis->port_a);
496 pHlp->pfnSSMGetU8(pSSMHandle, &pThis->port_b);
497 pHlp->pfnSSMGetU8(pSSMHandle, &pThis->port_c);
498 pHlp->pfnSSMGetU8(pSSMHandle, &pThis->ctrl_port);
499 /* Other device state. */
500 pHlp->pfnSSMGetU8(pSSMHandle, &pThis->cnt_held);
501 pHlp->pfnSSMGetU8(pSSMHandle, &pThis->held_dx);
502 pHlp->pfnSSMGetU8(pSSMHandle, &pThis->held_dy);
503 pHlp->pfnSSMGetU8(pSSMHandle, &pThis->irq);
504 pHlp->pfnSSMGetU32(pSSMHandle, &pThis->cTimerPeriodMs);
505 /* Current mouse state deltas. */
506 pHlp->pfnSSMGetS32(pSSMHandle, &pThis->mouse_dx);
507 pHlp->pfnSSMGetS32(pSSMHandle, &pThis->mouse_dy);
508 pHlp->pfnSSMGetU8(pSSMHandle, &pThis->mouse_buttons_reported);
509 /* Timer. */
510 return PDMDevHlpTimerLoad(pDevIns, pThis->hMouseTimer, pSSMHandle);
511}
512
513/**
514 * Reset notification.
515 *
516 * @returns VBox status code.
517 * @param pDevIns The device instance data.
518 */
519static DECLCALLBACK(void) bmsR3Reset(PPDMDEVINS pDevIns)
520{
521 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
522 PBMSSTATER3 pThisCC = PDMDEVINS_2_DATA(pDevIns, PBMSSTATER3);
523
524 /* Reinitialize the timer. */
525 pThis->cTimerPeriodMs = BMS_IRQ_PERIOD_MS / 2;
526 PDMDevHlpTimerSetMillies(pDevIns, pThis->hMouseTimer, pThis->cTimerPeriodMs);
527
528 /* Clear the device setup. */
529 pThis->port_a = pThis->port_b = 0;
530 pThis->port_c = BMS_CTL_INT_DIS; /* Interrupts disabled. */
531 pThis->ctrl_port = 0x91; /* Default 8255A setup. */
532
533 /* Clear motion/button state. */
534 pThis->cnt_held = false;
535 pThis->mouse_dx = pThis->mouse_dy = 0;
536 pThis->mouse_buttons = 0;
537 pThis->mouse_buttons_reported = 0;
538 pThis->disable_counter = 0;
539 pThis->irq_toggle_counter = 1000;
540
541 if (pThis->mouse_enabled)
542 {
543 pThis->mouse_enabled = false;
544 bmsR3UpdateDownstreamStatus(pThis, pThisCC);
545 }
546}
547
548
549/* -=-=-=-=-=- Mouse: IBase -=-=-=-=-=- */
550
551/**
552 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
553 */
554static DECLCALLBACK(void *) bmsR3Base_QueryMouseInterface(PPDMIBASE pInterface, const char *pszIID)
555{
556 PBMSSTATER3 pThisCC = RT_FROM_MEMBER(pInterface, BMSSTATER3, Mouse.IBase);
557 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->Mouse.IBase);
558 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUSEPORT, &pThisCC->Mouse.IPort);
559 return NULL;
560}
561
562
563/* -=-=-=-=-=- Mouse: IMousePort -=-=-=-=-=- */
564
565/**
566 * @interface_method_impl{PDMIMOUSEPORT,pfnPutEvent}
567 */
568static DECLCALLBACK(int) bmsR3MousePort_PutEvent(PPDMIMOUSEPORT pInterface, int32_t dx,
569 int32_t dy, int32_t dz, int32_t dw,
570 uint32_t fButtons)
571{
572 PBMSSTATER3 pThisCC = RT_FROM_MEMBER(pInterface, BMSSTATER3, Mouse.IPort);
573 PPDMDEVINS pDevIns = pThisCC->pDevIns;
574 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
575 int rc = PDMDevHlpCritSectEnter(pDevIns, pDevIns->CTX_SUFF(pCritSectRo), VERR_SEM_BUSY);
576 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->CTX_SUFF(pCritSectRo), rc);
577
578 bmsR3MouseEvent(pThis, dx, dy, dz, dw, fButtons);
579
580 PDMDevHlpCritSectLeave(pDevIns, pDevIns->CTX_SUFF(pCritSectRo));
581 return VINF_SUCCESS;
582}
583
584/**
585 * @interface_method_impl{PDMIMOUSEPORT,pfnPutEventAbs}
586 */
587static DECLCALLBACK(int) bmsR3MousePort_PutEventAbs(PPDMIMOUSEPORT pInterface, uint32_t x, uint32_t y,
588 int32_t dz, int32_t dw, uint32_t fButtons)
589{
590 RT_NOREF(pInterface, x, y, dz, dw, fButtons);
591 AssertFailedReturn(VERR_NOT_SUPPORTED);
592}
593
594/**
595 * @interface_method_impl{PDMIMOUSEPORT,pfnPutEventMultiTouch}
596 */
597static DECLCALLBACK(int) bmsR3MousePort_PutEventMultiTouch(PPDMIMOUSEPORT pInterface, uint8_t cContacts,
598 const uint64_t *pau64Contacts, uint32_t u32ScanTime)
599{
600 RT_NOREF(pInterface, cContacts, pau64Contacts, u32ScanTime);
601 AssertFailedReturn(VERR_NOT_SUPPORTED);
602}
603
604/* -=-=-=-=-=- setup code -=-=-=-=-=- */
605
606
607/**
608 * @interface_method_impl{PDMDEVREGR3,pfnAttach}
609 */
610static DECLCALLBACK(int) bmsR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
611{
612 PBMSSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PBMSSTATER3);
613 int rc;
614
615 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
616 ("Bus mouse device does not support hotplugging\n"),
617 VERR_INVALID_PARAMETER);
618
619 switch (iLUN)
620 {
621 /* LUN #0: mouse */
622 case 0:
623 rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThisCC->Mouse.IBase, &pThisCC->Mouse.pDrvBase, "Bus Mouse Port");
624 if (RT_SUCCESS(rc))
625 {
626 pThisCC->Mouse.pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->Mouse.pDrvBase, PDMIMOUSECONNECTOR);
627 if (!pThisCC->Mouse.pDrv)
628 {
629 AssertLogRelMsgFailed(("LUN #0 doesn't have a mouse interface! rc=%Rrc\n", rc));
630 rc = VERR_PDM_MISSING_INTERFACE;
631 }
632 }
633 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
634 {
635 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
636 rc = VINF_SUCCESS;
637 }
638 else
639 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
640 break;
641
642 default:
643 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
644 return VERR_PDM_NO_SUCH_LUN;
645 }
646
647 return rc;
648}
649
650
651/**
652 * @interface_method_impl{PDMDEVREGR3,pfnDetach}
653 */
654static DECLCALLBACK(void) bmsR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
655{
656# if 0
657 /*
658 * Reset the interfaces and update the controller state.
659 */
660 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
661 switch (iLUN)
662 {
663 /* LUN #0: mouse */
664 case 0:
665 pThis->Mouse.pDrv = NULL;
666 pThis->Mouse.pDrvBase = NULL;
667 break;
668
669 default:
670 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
671 break;
672 }
673# else
674 RT_NOREF(pDevIns, iLUN, fFlags);
675# endif
676}
677
678
679/**
680 * @interface_method_impl{PDMDEVREG,pfnConstruct}
681 */
682static DECLCALLBACK(int) bmsR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
683{
684 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
685 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
686 PBMSSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PBMSSTATER3);
687 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
688 int rc;
689 RT_NOREF(iInstance);
690
691 Assert(iInstance == 0);
692
693 /*
694 * Validate and read the configuration.
695 */
696 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "IRQ", "");
697
698 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IRQ", &pThis->irq, BMS_DEFAULT_IRQ);
699 if (RT_FAILURE(rc))
700 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"IRQ\" from the config"));
701 if (pThis->irq < 2 || pThis->irq > 5)
702 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Invalid \"IRQ\" config setting"));
703
704 Log(("busmouse: IRQ=%u fRCEnabled=%RTbool fR0Enabled=%RTbool\n", pThis->irq, pDevIns->fRCEnabled, pDevIns->fR0Enabled));
705
706 /*
707 * Initialize the interfaces.
708 */
709 pThisCC->pDevIns = pDevIns;
710 pThisCC->Mouse.IBase.pfnQueryInterface = bmsR3Base_QueryMouseInterface;
711 pThisCC->Mouse.IPort.pfnPutEvent = bmsR3MousePort_PutEvent;
712 pThisCC->Mouse.IPort.pfnPutEventAbs = bmsR3MousePort_PutEventAbs;
713 pThisCC->Mouse.IPort.pfnPutEventMultiTouch = bmsR3MousePort_PutEventMultiTouch;
714
715 /*
716 * Create the interrupt timer.
717 */
718 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, bmsR3TimerCallback, pThis,
719 TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "Bus Mouse", &pThis->hMouseTimer);
720 AssertRCReturn(rc, rc);
721
722 /*
723 * Register I/O ports.
724 */
725 static const IOMIOPORTDESC s_aDescs[] =
726 {
727 { "DATA", "DATA", NULL, NULL },
728 { "SIG", "SIG", NULL, NULL },
729 { "CTRL", "CTRL", NULL, NULL },
730 { "INIT", "INIT", NULL, NULL },
731 { NULL, NULL, NULL, NULL }
732 };
733 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, BMS_IO_BASE, BMS_IO_SIZE, bmsIoPortWrite, bmsIoPortRead,
734 "Bus Mouse", s_aDescs, &pThis->hIoPorts);
735 AssertRCReturn(rc, rc);
736
737 /*
738 * Register saved state.
739 */
740 rc = PDMDevHlpSSMRegister(pDevIns, BMS_SAVED_STATE_VERSION, sizeof(*pThis), bmsR3SaveExec, bmsR3LoadExec);
741 AssertRCReturn(rc, rc);
742
743 /*
744 * Attach to the mouse driver.
745 */
746 rc = bmsR3Attach(pDevIns, 0, PDM_TACH_FLAGS_NOT_HOT_PLUG);
747 AssertRCReturn(rc, rc);
748
749 /*
750 * Initialize the device state.
751 */
752 bmsR3Reset(pDevIns);
753
754 return VINF_SUCCESS;
755}
756
757# else /* !IN_RING3 */
758
759/**
760 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
761 */
762static DECLCALLBACK(int) bmsRZConstruct(PPDMDEVINS pDevIns)
763{
764 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
765 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
766
767 int rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPorts, bmsIoPortWrite, bmsIoPortRead, NULL /*pvUser*/);
768 AssertRCReturn(rc, rc);
769
770 return VINF_SUCCESS;
771}
772
773# endif /* !IN_RING3 */
774
775
776/**
777 * The device registration structure.
778 */
779static const PDMDEVREG g_DeviceBusMouse =
780{
781 /* .u32Version = */ PDM_DEVREG_VERSION,
782 /* .uReserved0 = */ 0,
783 /* .szName = */ "busmouse",
784 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS /** @todo | PDM_DEVREG_FLAGS_RZ */ | PDM_DEVREG_FLAGS_NEW_STYLE,
785 /* .fClass = */ PDM_DEVREG_CLASS_INPUT,
786 /* .cMaxInstances = */ 1,
787 /* .uSharedVersion = */ 42,
788 /* .cbInstanceShared = */ sizeof(BMSSTATE),
789 /* .cbInstanceCC = */ CTX_EXPR(sizeof(BMSSTATER3), 0, 0),
790 /* .cbInstanceRC = */ 0,
791 /* .cMaxPciDevices = */ 0,
792 /* .cMaxMsixVectors = */ 0,
793 /* .pszDescription = */ "Microsoft Bus Mouse controller. LUN #0 is the mouse connector.",
794# if defined(IN_RING3)
795 /* .pszRCMod = */ "VBoxDDRC.rc",
796 /* .pszR0Mod = */ "VBoxDDR0.r0",
797 /* .pfnConstruct = */ bmsR3Construct,
798 /* .pfnDestruct = */ NULL,
799 /* .pfnRelocate = */ NULL,
800 /* .pfnMemSetup = */ NULL,
801 /* .pfnPowerOn = */ NULL,
802 /* .pfnReset = */ bmsR3Reset,
803 /* .pfnSuspend = */ NULL,
804 /* .pfnResume = */ NULL,
805 /* .pfnAttach = */ bmsR3Attach,
806 /* .pfnDetach = */ bmsR3Detach,
807 /* .pfnQueryInterface = */ NULL,
808 /* .pfnInitComplete = */ NULL,
809 /* .pfnPowerOff = */ NULL,
810 /* .pfnSoftReset = */ NULL,
811 /* .pfnReserved0 = */ NULL,
812 /* .pfnReserved1 = */ NULL,
813 /* .pfnReserved2 = */ NULL,
814 /* .pfnReserved3 = */ NULL,
815 /* .pfnReserved4 = */ NULL,
816 /* .pfnReserved5 = */ NULL,
817 /* .pfnReserved6 = */ NULL,
818 /* .pfnReserved7 = */ NULL,
819# elif defined(IN_RING0)
820 /* .pfnEarlyConstruct = */ NULL,
821 /* .pfnConstruct = */ bmsRZConstruct,
822 /* .pfnDestruct = */ NULL,
823 /* .pfnFinalDestruct = */ NULL,
824 /* .pfnRequest = */ NULL,
825 /* .pfnReserved0 = */ NULL,
826 /* .pfnReserved1 = */ NULL,
827 /* .pfnReserved2 = */ NULL,
828 /* .pfnReserved3 = */ NULL,
829 /* .pfnReserved4 = */ NULL,
830 /* .pfnReserved5 = */ NULL,
831 /* .pfnReserved6 = */ NULL,
832 /* .pfnReserved7 = */ NULL,
833# elif defined(IN_RC)
834 /* .pfnConstruct = */ bmsRZConstruct,
835 /* .pfnReserved0 = */ NULL,
836 /* .pfnReserved1 = */ NULL,
837 /* .pfnReserved2 = */ NULL,
838 /* .pfnReserved3 = */ NULL,
839 /* .pfnReserved4 = */ NULL,
840 /* .pfnReserved5 = */ NULL,
841 /* .pfnReserved6 = */ NULL,
842 /* .pfnReserved7 = */ NULL,
843# else
844# error "Not in IN_RING3, IN_RING0 or IN_RC!"
845# endif
846 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
847};
848
849# ifdef VBOX_IN_EXTPACK_R3
850
851/**
852 * @callback_method_impl{FNPDMVBOXDEVICESREGISTER}
853 */
854extern "C" DECLEXPORT(int) VBoxDevicesRegister(PPDMDEVREGCB pCallbacks, uint32_t u32Version)
855{
856 AssertLogRelMsgReturn(u32Version >= VBOX_VERSION,
857 ("u32Version=%#x VBOX_VERSION=%#x\n", u32Version, VBOX_VERSION),
858 VERR_EXTPACK_VBOX_VERSION_MISMATCH);
859 AssertLogRelMsgReturn(pCallbacks->u32Version == PDM_DEVREG_CB_VERSION,
860 ("pCallbacks->u32Version=%#x PDM_DEVREG_CB_VERSION=%#x\n", pCallbacks->u32Version, PDM_DEVREG_CB_VERSION),
861 VERR_VERSION_MISMATCH);
862
863 return pCallbacks->pfnRegister(pCallbacks, &g_DeviceBusMouse);
864}
865
866# else /* !VBOX_IN_EXTPACK_R3 */
867
868/** Pointer to the ring-0 device registrations for the Bus Mouse. */
869static PCPDMDEVREGR0 g_apDevRegs[] =
870{
871 &g_DeviceBusMouse,
872};
873
874/** Module device registration record for the Bus Mouse. */
875static PDMDEVMODREGR0 g_ModDevReg =
876{
877 /* .u32Version = */ PDM_DEVMODREGR0_VERSION,
878 /* .cDevRegs = */ RT_ELEMENTS(g_apDevRegs),
879 /* .papDevRegs = */ &g_apDevRegs[0],
880 /* .hMod = */ NULL,
881 /* .ListEntry = */ { NULL, NULL },
882};
883
884DECLEXPORT(int) ModuleInit(void *hMod)
885{
886 LogFlow(("VBoxBusMouseRZ/ModuleInit: %p\n", hMod));
887 return PDMR0DeviceRegisterModule(hMod, &g_ModDevReg);
888}
889
890DECLEXPORT(void) ModuleTerm(void *hMod)
891{
892 LogFlow(("VBoxBusMouseRZ/ModuleTerm: %p\n", hMod));
893 PDMR0DeviceDeregisterModule(hMod, &g_ModDevReg);
894}
895
896# endif /* !VBOX_IN_EXTPACK_R3 */
897
898#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
899
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