VirtualBox

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

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

DevBusMouse: Un-broke the code after PDM interface changes. Also avoided possible trouble when Mouse.pDrv is not set in case device is misconfigured and has no driver attached.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.0 KB
Line 
1/* $Id: DevBusMouse.cpp 94987 2022-05-12 09:05:55Z 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 if (pDrv) /* pDrv may be NULL if no mouse interface attached. */
220 pDrv->pfnReportModes(pDrv, fEnabled, false, false);
221}
222
223/**
224 * Process a mouse event coming from the host.
225 */
226static void bmsR3MouseEvent(PBMSSTATE pThis, int dx, int dy, int dz, int dw, int buttons_state)
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. */
232 pThis->mouse_dx += dx;
233 pThis->mouse_dy += dy;
234 pThis->mouse_buttons = buttons_state;
235}
236
237/**
238 * @callback_method_impl{FNTMTIMERDEV}
239 */
240static DECLCALLBACK(void) bmsR3TimerCallback(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
241{
242 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
243 PBMSSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PBMSSTATER3);
244 uint8_t irq_bit;
245 RT_NOREF(pvUser);
246 Assert(hTimer == pThis->hMouseTimer);
247
248 /* Toggle the IRQ line if interrupts are enabled. */
249 irq_bit = BMS_IRQ_BIT(pThis->irq);
250
251 if (pThis->port_c & irq_bit)
252 {
253 if (!(pThis->port_c & BMS_CTL_INT_DIS))
254 PDMDevHlpISASetIrq(pDevIns, pThis->irq, PDM_IRQ_LEVEL_LOW);
255 pThis->port_c &= ~irq_bit;
256 }
257 else
258 {
259 pThis->port_c |= irq_bit;
260 if (!(pThis->port_c & BMS_CTL_INT_DIS))
261 PDMDevHlpISASetIrq(pDevIns, pThis->irq, PDM_IRQ_LEVEL_HIGH);
262 }
263
264 /* Handle enabling/disabling of the mouse interface. */
265 if (pThis->port_c & BMS_CTL_INT_DIS)
266 {
267 if (pThis->disable_counter)
268 --pThis->disable_counter;
269
270 if (pThis->disable_counter == 0 && pThis->mouse_enabled)
271 {
272 pThis->mouse_enabled = false;
273 bmsR3UpdateDownstreamStatus(pThis, pThisCC);
274 }
275 }
276 else
277 {
278 pThis->disable_counter = 8; /* Re-arm the disable countdown. */
279 if (!pThis->mouse_enabled)
280 {
281 pThis->mouse_enabled = true;
282 bmsR3UpdateDownstreamStatus(pThis, pThisCC);
283 }
284 }
285
286 /* Re-arm the timer. */
287 PDMDevHlpTimerSetMillies(pDevIns, hTimer, pThis->cTimerPeriodMs);
288}
289
290# endif /* IN_RING3 */
291
292static void bmsSetReportedButtons(PBMSSTATE pThis, unsigned fButtons, unsigned fButtonMask)
293{
294 pThis->mouse_buttons_reported |= (fButtons & fButtonMask);
295 pThis->mouse_buttons_reported &= (fButtons | ~fButtonMask);
296}
297
298/**
299 * Update the internal state after a write to port C.
300 */
301static void bmsUpdateCtrl(PPDMDEVINS pDevIns, PBMSSTATE pThis)
302{
303 int32_t dx, dy;
304
305 /* If the controller is in hold state, transfer data from counters. */
306 if (pThis->port_c & BMS_CTL_HOLD)
307 {
308 if (!pThis->cnt_held)
309 {
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;
317 bmsSetReportedButtons(pThis, pThis->mouse_buttons & 0x07, 0x07);
318
319 /* Force type conversion. */
320 pThis->held_dx = dx;
321 pThis->held_dy = dy;
322 }
323 }
324 else
325 pThis->cnt_held = false;
326
327 /* Move the appropriate nibble into port A. */
328 if (pThis->cnt_held)
329 {
330 if (pThis->port_c & BMS_CTL_SEL_Y)
331 {
332 if (pThis->port_c & BMS_CTL_SEL_HIGH)
333 pThis->port_a = pThis->held_dy >> 4;
334 else
335 pThis->port_a = pThis->held_dy & 0xF;
336 }
337 else
338 {
339 if (pThis->port_c & BMS_CTL_SEL_HIGH)
340 pThis->port_a = pThis->held_dx >> 4;
341 else
342 pThis->port_a = pThis->held_dx & 0xF;
343 }
344 /* And update the button bits. */
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;
348 }
349 /* Immediately clear the IRQ if necessary. */
350 if (pThis->port_c & BMS_CTL_INT_DIS)
351 {
352 PDMDevHlpISASetIrq(pDevIns, pThis->irq, PDM_IRQ_LEVEL_LOW);
353 pThis->port_c &= ~BMS_IRQ_BIT(pThis->irq);
354 }
355}
356
357/**
358 * @callback_method_impl{FNIOMIOPORTNEWIN}
359 */
360static DECLCALLBACK(VBOXSTRICTRC) bmsIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
361{
362 RT_NOREF(pvUser);
363 if (cb == 1)
364 {
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));
407 return VINF_SUCCESS;
408 }
409 ASSERT_GUEST_MSG_FAILED(("offPort=%#x cb=%d\n", offPort, cb));
410 return VERR_IOM_IOPORT_UNUSED;
411}
412
413/**
414 * @callback_method_impl{FNIOMIOPORTNEWOUT}
415 */
416static DECLCALLBACK(VBOXSTRICTRC) bmsIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
417{
418 RT_NOREF(pvUser);
419 if (cb == 1)
420 {
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
446 Log2(("mouIoPortWrite: offPort=%#x+%u cb=%d u32=%#x\n", BMS_IO_BASE, offPort, cb, u32));
447 }
448 else
449 ASSERT_GUEST_MSG_FAILED(("offPort=%#x cb=%d\n", offPort, cb));
450 return VINF_SUCCESS;
451}
452
453# ifdef IN_RING3
454
455/**
456 * @callback_method_impl{FNSSMDEVSAVEEXEC}
457 */
458static DECLCALLBACK(int) bmsR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
459{
460 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
461 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
462
463 /* 8255A state. */
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);
468 /* Other device state. */
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);
474 /* Current mouse state deltas. */
475 pHlp->pfnSSMPutS32(pSSMHandle, pThis->mouse_dx);
476 pHlp->pfnSSMPutS32(pSSMHandle, pThis->mouse_dy);
477 pHlp->pfnSSMPutU8(pSSMHandle, pThis->mouse_buttons_reported);
478 /* Timer. */
479 return PDMDevHlpTimerSave(pDevIns, pThis->hMouseTimer, pSSMHandle);
480}
481
482/**
483 * @callback_method_impl{FNSSMDEVLOADEXEC}
484 */
485static DECLCALLBACK(int) bmsR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t uVersion, uint32_t uPass)
486{
487 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
488 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
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. */
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);
500 /* Other device state. */
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);
506 /* Current mouse state deltas. */
507 pHlp->pfnSSMGetS32(pSSMHandle, &pThis->mouse_dx);
508 pHlp->pfnSSMGetS32(pSSMHandle, &pThis->mouse_dy);
509 pHlp->pfnSSMGetU8(pSSMHandle, &pThis->mouse_buttons_reported);
510 /* Timer. */
511 return PDMDevHlpTimerLoad(pDevIns, pThis->hMouseTimer, pSSMHandle);
512}
513
514/**
515 * Reset notification.
516 *
517 * @returns VBox status code.
518 * @param pDevIns The device instance data.
519 */
520static DECLCALLBACK(void) bmsR3Reset(PPDMDEVINS pDevIns)
521{
522 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
523 PBMSSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PBMSSTATER3);
524
525 /* Reinitialize the timer. */
526 pThis->cTimerPeriodMs = BMS_IRQ_PERIOD_MS / 2;
527 PDMDevHlpTimerSetMillies(pDevIns, pThis->hMouseTimer, pThis->cTimerPeriodMs);
528
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 }
547}
548
549
550/* -=-=-=-=-=- Mouse: IBase -=-=-=-=-=- */
551
552/**
553 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
554 */
555static DECLCALLBACK(void *) bmsR3Base_QueryMouseInterface(PPDMIBASE pInterface, const char *pszIID)
556{
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);
560 return NULL;
561}
562
563
564/* -=-=-=-=-=- Mouse: IMousePort -=-=-=-=-=- */
565
566/**
567 * @interface_method_impl{PDMIMOUSEPORT,pfnPutEvent}
568 */
569static DECLCALLBACK(int) bmsR3MousePort_PutEvent(PPDMIMOUSEPORT pInterface, int32_t dx,
570 int32_t dy, int32_t dz, int32_t dw,
571 uint32_t fButtons)
572{
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);
577 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->CTX_SUFF(pCritSectRo), rc);
578
579 bmsR3MouseEvent(pThis, dx, dy, dz, dw, fButtons);
580
581 PDMDevHlpCritSectLeave(pDevIns, pDevIns->CTX_SUFF(pCritSectRo));
582 return VINF_SUCCESS;
583}
584
585/**
586 * @interface_method_impl{PDMIMOUSEPORT,pfnPutEventAbs}
587 */
588static DECLCALLBACK(int) bmsR3MousePort_PutEventAbs(PPDMIMOUSEPORT pInterface, uint32_t x, uint32_t y,
589 int32_t dz, int32_t dw, uint32_t fButtons)
590{
591 RT_NOREF(pInterface, x, y, dz, dw, fButtons);
592 AssertFailedReturn(VERR_NOT_SUPPORTED);
593}
594
595/**
596 * @interface_method_impl{PDMIMOUSEPORT,pfnPutEventMultiTouch}
597 */
598static DECLCALLBACK(int) bmsR3MousePort_PutEventMultiTouch(PPDMIMOUSEPORT pInterface, uint8_t cContacts,
599 const uint64_t *pau64Contacts, uint32_t u32ScanTime)
600{
601 RT_NOREF(pInterface, cContacts, pau64Contacts, u32ScanTime);
602 AssertFailedReturn(VERR_NOT_SUPPORTED);
603}
604
605/* -=-=-=-=-=- setup code -=-=-=-=-=- */
606
607
608/**
609 * @interface_method_impl{PDMDEVREGR3,pfnAttach}
610 */
611static DECLCALLBACK(int) bmsR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
612{
613 PBMSSTATER3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PBMSSTATER3);
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:
624 rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThisCC->Mouse.IBase, &pThisCC->Mouse.pDrvBase, "Bus Mouse Port");
625 if (RT_SUCCESS(rc))
626 {
627 pThisCC->Mouse.pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->Mouse.pDrvBase, PDMIMOUSECONNECTOR);
628 if (!pThisCC->Mouse.pDrv)
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 {
636 LogRel(("%s/%d: Warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
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/**
653 * @interface_method_impl{PDMDEVREGR3,pfnDetach}
654 */
655static DECLCALLBACK(void) bmsR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
656{
657# if 0
658 /*
659 * Reset the interfaces and update the controller state.
660 */
661 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
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 }
674# else
675 RT_NOREF(pDevIns, iLUN, fFlags);
676# endif
677}
678
679
680/**
681 * @interface_method_impl{PDMDEVREG,pfnConstruct}
682 */
683static DECLCALLBACK(int) bmsR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
684{
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;
690 RT_NOREF(iInstance);
691
692 Assert(iInstance == 0);
693
694 /*
695 * Validate and read the configuration.
696 */
697 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "IRQ", "");
698
699 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IRQ", &pThis->irq, BMS_DEFAULT_IRQ);
700 if (RT_FAILURE(rc))
701 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"IRQ\" from the config"));
702 if (pThis->irq < 2 || pThis->irq > 5)
703 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Invalid \"IRQ\" config setting"));
704
705 Log(("busmouse: IRQ=%u fRCEnabled=%RTbool fR0Enabled=%RTbool\n", pThis->irq, pDevIns->fRCEnabled, pDevIns->fR0Enabled));
706
707 /*
708 * Initialize the interfaces.
709 */
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;
714 pThisCC->Mouse.IPort.pfnPutEventMultiTouch = bmsR3MousePort_PutEventMultiTouch;
715
716 /*
717 * Create the interrupt timer.
718 */
719 rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, bmsR3TimerCallback, pThis,
720 TMTIMER_FLAGS_DEFAULT_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "Bus Mouse", &pThis->hMouseTimer);
721 AssertRCReturn(rc, rc);
722
723 /*
724 * Register I/O ports.
725 */
726 static const IOMIOPORTDESC s_aDescs[] =
727 {
728 { "DATA", "DATA", NULL, NULL },
729 { "SIG", "SIG", NULL, NULL },
730 { "CTRL", "CTRL", NULL, NULL },
731 { "INIT", "INIT", NULL, NULL },
732 { NULL, NULL, NULL, NULL }
733 };
734 rc = PDMDevHlpIoPortCreateAndMap(pDevIns, BMS_IO_BASE, BMS_IO_SIZE, bmsIoPortWrite, bmsIoPortRead,
735 "Bus Mouse", s_aDescs, &pThis->hIoPorts);
736 AssertRCReturn(rc, rc);
737
738 /*
739 * Register saved state.
740 */
741 rc = PDMDevHlpSSMRegister(pDevIns, BMS_SAVED_STATE_VERSION, sizeof(*pThis), bmsR3SaveExec, bmsR3LoadExec);
742 AssertRCReturn(rc, rc);
743
744 /*
745 * Attach to the mouse driver.
746 */
747 rc = bmsR3Attach(pDevIns, 0, PDM_TACH_FLAGS_NOT_HOT_PLUG);
748 AssertRCReturn(rc, rc);
749
750 /*
751 * Initialize the device state.
752 */
753 bmsR3Reset(pDevIns);
754
755 return VINF_SUCCESS;
756}
757
758# else /* !IN_RING3 */
759
760/**
761 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
762 */
763static DECLCALLBACK(int) bmsRZConstruct(PPDMDEVINS pDevIns)
764{
765 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
766 PBMSSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PBMSSTATE);
767
768 int rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPorts, bmsIoPortWrite, bmsIoPortRead, NULL /*pvUser*/);
769 AssertRCReturn(rc, rc);
770
771 return VINF_SUCCESS;
772}
773
774# endif /* !IN_RING3 */
775
776
777/**
778 * The device registration structure.
779 */
780static const PDMDEVREG g_DeviceBusMouse =
781{
782 /* .u32Version = */ PDM_DEVREG_VERSION,
783 /* .uReserved0 = */ 0,
784 /* .szName = */ "busmouse",
785 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS /** @todo | PDM_DEVREG_FLAGS_RZ */ | PDM_DEVREG_FLAGS_NEW_STYLE,
786 /* .fClass = */ PDM_DEVREG_CLASS_INPUT,
787 /* .cMaxInstances = */ 1,
788 /* .uSharedVersion = */ 42,
789 /* .cbInstanceShared = */ sizeof(BMSSTATE),
790 /* .cbInstanceCC = */ CTX_EXPR(sizeof(BMSSTATER3), 0, 0),
791 /* .cbInstanceRC = */ 0,
792 /* .cMaxPciDevices = */ 0,
793 /* .cMaxMsixVectors = */ 0,
794 /* .pszDescription = */ "Microsoft Bus Mouse controller. LUN #0 is the mouse connector.",
795# if defined(IN_RING3)
796 /* .pszRCMod = */ "VBoxDDRC.rc",
797 /* .pszR0Mod = */ "VBoxDDR0.r0",
798 /* .pfnConstruct = */ bmsR3Construct,
799 /* .pfnDestruct = */ NULL,
800 /* .pfnRelocate = */ NULL,
801 /* .pfnMemSetup = */ NULL,
802 /* .pfnPowerOn = */ NULL,
803 /* .pfnReset = */ bmsR3Reset,
804 /* .pfnSuspend = */ NULL,
805 /* .pfnResume = */ NULL,
806 /* .pfnAttach = */ bmsR3Attach,
807 /* .pfnDetach = */ bmsR3Detach,
808 /* .pfnQueryInterface = */ NULL,
809 /* .pfnInitComplete = */ NULL,
810 /* .pfnPowerOff = */ NULL,
811 /* .pfnSoftReset = */ NULL,
812 /* .pfnReserved0 = */ NULL,
813 /* .pfnReserved1 = */ NULL,
814 /* .pfnReserved2 = */ NULL,
815 /* .pfnReserved3 = */ NULL,
816 /* .pfnReserved4 = */ NULL,
817 /* .pfnReserved5 = */ NULL,
818 /* .pfnReserved6 = */ NULL,
819 /* .pfnReserved7 = */ NULL,
820# elif defined(IN_RING0)
821 /* .pfnEarlyConstruct = */ NULL,
822 /* .pfnConstruct = */ bmsRZConstruct,
823 /* .pfnDestruct = */ NULL,
824 /* .pfnFinalDestruct = */ NULL,
825 /* .pfnRequest = */ NULL,
826 /* .pfnReserved0 = */ NULL,
827 /* .pfnReserved1 = */ NULL,
828 /* .pfnReserved2 = */ NULL,
829 /* .pfnReserved3 = */ NULL,
830 /* .pfnReserved4 = */ NULL,
831 /* .pfnReserved5 = */ NULL,
832 /* .pfnReserved6 = */ NULL,
833 /* .pfnReserved7 = */ NULL,
834# elif defined(IN_RC)
835 /* .pfnConstruct = */ bmsRZConstruct,
836 /* .pfnReserved0 = */ NULL,
837 /* .pfnReserved1 = */ NULL,
838 /* .pfnReserved2 = */ NULL,
839 /* .pfnReserved3 = */ NULL,
840 /* .pfnReserved4 = */ NULL,
841 /* .pfnReserved5 = */ NULL,
842 /* .pfnReserved6 = */ NULL,
843 /* .pfnReserved7 = */ NULL,
844# else
845# error "Not in IN_RING3, IN_RING0 or IN_RC!"
846# endif
847 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
848};
849
850# ifdef VBOX_IN_EXTPACK_R3
851
852/**
853 * @callback_method_impl{FNPDMVBOXDEVICESREGISTER}
854 */
855extern "C" DECLEXPORT(int) VBoxDevicesRegister(PPDMDEVREGCB pCallbacks, uint32_t u32Version)
856{
857 AssertLogRelMsgReturn(u32Version >= VBOX_VERSION,
858 ("u32Version=%#x VBOX_VERSION=%#x\n", u32Version, VBOX_VERSION),
859 VERR_EXTPACK_VBOX_VERSION_MISMATCH);
860 AssertLogRelMsgReturn(pCallbacks->u32Version == PDM_DEVREG_CB_VERSION,
861 ("pCallbacks->u32Version=%#x PDM_DEVREG_CB_VERSION=%#x\n", pCallbacks->u32Version, PDM_DEVREG_CB_VERSION),
862 VERR_VERSION_MISMATCH);
863
864 return pCallbacks->pfnRegister(pCallbacks, &g_DeviceBusMouse);
865}
866
867# else /* !VBOX_IN_EXTPACK_R3 */
868
869/** Pointer to the ring-0 device registrations for the Bus Mouse. */
870static PCPDMDEVREGR0 g_apDevRegs[] =
871{
872 &g_DeviceBusMouse,
873};
874
875/** Module device registration record for the Bus Mouse. */
876static PDMDEVMODREGR0 g_ModDevReg =
877{
878 /* .u32Version = */ PDM_DEVMODREGR0_VERSION,
879 /* .cDevRegs = */ RT_ELEMENTS(g_apDevRegs),
880 /* .papDevRegs = */ &g_apDevRegs[0],
881 /* .hMod = */ NULL,
882 /* .ListEntry = */ { NULL, NULL },
883};
884
885DECLEXPORT(int) ModuleInit(void *hMod)
886{
887 LogFlow(("VBoxBusMouseRZ/ModuleInit: %p\n", hMod));
888 return PDMR0DeviceRegisterModule(hMod, &g_ModDevReg);
889}
890
891DECLEXPORT(void) ModuleTerm(void *hMod)
892{
893 LogFlow(("VBoxBusMouseRZ/ModuleTerm: %p\n", hMod));
894 PDMR0DeviceDeregisterModule(hMod, &g_ModDevReg);
895}
896
897# endif /* !VBOX_IN_EXTPACK_R3 */
898
899#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
900
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