VirtualBox

source: vbox/trunk/src/VBox/ExtPacks/BusMouseSample/BusMouse.cpp@ 79773

Last change on this file since 79773 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.7 KB
Line 
1/* $Id: BusMouse.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * BusMouse - Microsoft Bus (parallel) mouse controller device.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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#include <VBox/version.h>
38#include <iprt/assert.h>
39#include <iprt/uuid.h>
40
41/** @page pg_busmouse DevBusMouse - Microsoft Bus Mouse Emulation
42 *
43 * The Microsoft Bus Mouse was an early mouse sold by Microsoft, originally
44 * introduced in 1983. The mouse had a D-shaped 9-pin connector which plugged
45 * into a small ISA add-in board.
46 *
47 * The mouse itself was very simple (compared to a serial mouse) and most of the
48 * logic was located on the ISA board. Later, Microsoft sold an InPort mouse,
49 * which was also called a "bus mouse", but used a different interface.
50 *
51 * Microsoft part numbers for the Bus Mouse were 037-099 (100 ppi)
52 * and 037-199 (200 ppi).
53 *
54 * The Bus Mouse adapter included IRQ configuration jumpers (ref. MS article
55 * Q12230). The IRQ could be set to one of 2, 3, 4, 5. The typical setting
56 * would be IRQ 2 for a PC/XT and IRQ 5 for an AT compatible. Because IRQ 5
57 * may conflict with a SoundBlaster or a PCI device, this device defaults to
58 * IRQ 3. Note that IRQ 3 is also used by the COM 2 device, not often needed.
59 *
60 * The ISA adapter was built around an Intel 8255A compatible chip (ref.
61 * MS article Q46369). Once enabled, the adapter raises the configured IRQ
62 * 30 times per second; the rate is not configurable. The interrupts
63 * occur regardless of whether the mouse state has changed or not.
64 *
65 * To function properly, the 8255A must be programmed as follows:
66 * - Port A: Input. Used to read motion deltas and button states.
67 * - Port B: Output. Not used except for mouse detection.
68 * - Port C: Split. Upper bits set as output, used for control purposes.
69 * Lower bits set as input, reflecting IRQ state.
70 *
71 * Detailed information was gleaned from Windows and OS/2 DDK mouse samples.
72 */
73
74
75/*********************************************************************************************************************************
76* Defined Constants And Macros *
77*********************************************************************************************************************************/
78/** The original bus mouse controller is fixed at I/O port 0x23C. */
79#define BMS_IO_BASE 0x23C
80#define BMS_IO_SIZE 4
81
82/** @name Offsets relative to the I/O base.
83 *@{ */
84#define BMS_PORT_DATA 0 /**< 8255 Port A. */
85#define BMS_PORT_SIG 1 /**< 8255 Port B. */
86#define BMS_PORT_CTRL 2 /**< 8255 Port C. */
87#define BMS_PORT_INIT 3 /**< 8255 Control Port. */
88/** @} */
89
90/** @name Port C bits (control port).
91 * @{ */
92#define BMS_CTL_INT_DIS RT_BIT(4) /**< Disable IRQ (else enabled). */
93#define BMS_CTL_SEL_HIGH RT_BIT(5) /**< Select hi nibble (else lo). */
94#define BMS_CTL_SEL_Y RT_BIT(6) /**< Select X to read (else Y). */
95#define BMS_CTL_HOLD RT_BIT(7) /**< Hold counter (else clear). */
96/** @} */
97
98/** @name Port A bits (data port).
99 * @{ */
100#define BMS_DATA_DELTA 0x0F /**< Motion delta in lower nibble. */
101#define BMS_DATA_B3_UP RT_BIT(5) /**< Button 3 (right) is up. */
102#define BMS_DATA_B2_UP RT_BIT(6) /**< Button 2 (middle) is up. */
103#define BMS_DATA_B1_UP RT_BIT(7) /**< Button 1 (left) is up. */
104/** @} */
105
106/** Convert IRQ level (2/3/4/5) to a bit in the control register. */
107#define BMS_IRQ_BIT(a) (1 << (5 - a))
108
109/** IRQ period, corresponds to approx. 30 Hz. */
110#define BMS_IRQ_PERIOD_MS 34
111
112/** Default IRQ setting. */
113#define BMS_DEFAULT_IRQ 3
114
115/** The saved state version. */
116#define BMS_SAVED_STATE_VERSION 1
117
118
119/*********************************************************************************************************************************
120* Structures and Typedefs *
121*********************************************************************************************************************************/
122/**
123 * The device state.
124 */
125typedef struct MouState
126{
127 /** @name 8255A state
128 * @{ */
129 uint8_t port_a;
130 uint8_t port_b;
131 uint8_t port_c;
132 uint8_t ctrl_port;
133 uint8_t cnt_held; /**< Counters held for reading. */
134 uint8_t held_dx;
135 uint8_t held_dy;
136 uint8_t irq; /**< The "jumpered" IRQ level. */
137 int32_t irq_toggle_counter;
138 /** Mouse timer handle - HC. */
139 PTMTIMERR3 MouseTimer;
140 /** Timer period in milliseconds. */
141 uint32_t cTimerPeriodMs;
142 /** @} */
143
144 /** @name mouse state
145 * @{ */
146 int32_t disable_counter;
147 uint8_t mouse_enabled;
148 int32_t mouse_dx; /* current values, needed for 'poll' mode */
149 int32_t mouse_dy;
150 uint8_t mouse_buttons;
151 uint8_t mouse_buttons_reported;
152 /** @} */
153
154 /** Pointer to the device instance - RC. */
155 PPDMDEVINSRC pDevInsRC;
156 /** Pointer to the device instance - R3 . */
157 PPDMDEVINSR3 pDevInsR3;
158 /** Pointer to the device instance. */
159 PPDMDEVINSR0 pDevInsR0;
160
161 /**
162 * Mouse port - LUN#0.
163 *
164 * @implements PDMIBASE
165 * @implements PDMIMOUSEPORT
166 */
167 struct
168 {
169 /** The base interface for the mouse port. */
170 PDMIBASE IBase;
171 /** The mouse port base interface. */
172 PDMIMOUSEPORT IPort;
173
174 /** The base interface of the attached mouse driver. */
175 R3PTRTYPE(PPDMIBASE) pDrvBase;
176 /** The mouse interface of the attached mouse driver. */
177 R3PTRTYPE(PPDMIMOUSECONNECTOR) pDrv;
178 } Mouse;
179} MouState;
180
181
182
183#ifndef VBOX_DEVICE_STRUCT_TESTCASE
184
185# ifdef IN_RING3
186
187/**
188 * Report a change in status down the driver chain.
189 *
190 * We want to report the mouse as enabled if and only if the guest is "using"
191 * it. That way, other devices (e.g. a PS/2 or USB mouse) can receive mouse
192 * events when the bus mouse is disabled. Enabling interrupts constitutes
193 * enabling the bus mouse. The mouse is considered disabled if interrupts are
194 * disabled for several consecutive mouse timer ticks; this is because the
195 * interrupt handler in the guest typically temporarily disables interrupts and
196 * we do not want to toggle the enabled/disabled state more often than
197 * necessary.
198 */
199static void bms_update_downstream_status(MouState *pThis)
200{
201 PPDMIMOUSECONNECTOR pDrv = pThis->Mouse.pDrv;
202 bool fEnabled = !!pThis->mouse_enabled;
203 pDrv->pfnReportModes(pDrv, fEnabled, false, false);
204}
205
206/**
207 * Set the emulated hardware to a known initial state.
208 */
209static void bms_reset(MouState *pThis)
210{
211 /* Clear the device setup. */
212 pThis->port_a = pThis->port_b = 0;
213 pThis->port_c = BMS_CTL_INT_DIS; /* Interrupts disabled. */
214 pThis->ctrl_port = 0x91; /* Default 8255A setup. */
215
216 /* Clear motion/button state. */
217 pThis->cnt_held = false;
218 pThis->mouse_dx = pThis->mouse_dy = 0;
219 pThis->mouse_buttons = 0;
220 pThis->mouse_buttons_reported = 0;
221 pThis->disable_counter = 0;
222 pThis->irq_toggle_counter = 1000;
223
224 if (pThis->mouse_enabled)
225 {
226 pThis->mouse_enabled = false;
227 bms_update_downstream_status(pThis);
228 }
229}
230
231/* Process a mouse event coming from the host. */
232static void bms_mouse_event(MouState *pThis, int dx, int dy, int dz, int dw,
233 int buttons_state)
234{
235 LogRel3(("%s: dx=%d, dy=%d, dz=%d, dw=%d, buttons_state=0x%x\n",
236 __PRETTY_FUNCTION__, dx, dy, dz, dw, buttons_state));
237
238 /* Only record X/Y movement and buttons. */
239 pThis->mouse_dx += dx;
240 pThis->mouse_dy += dy;
241 pThis->mouse_buttons = buttons_state;
242}
243
244static DECLCALLBACK(void) bmsTimerCallback(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
245{
246 RT_NOREF(pvUser);
247 MouState *pThis = PDMINS_2_DATA(pDevIns, MouState *);
248 uint8_t irq_bit;
249
250 /* Toggle the IRQ line if interrupts are enabled. */
251 irq_bit = BMS_IRQ_BIT(pThis->irq);
252
253 if (pThis->port_c & irq_bit)
254 {
255 if (!(pThis->port_c & BMS_CTL_INT_DIS))
256 PDMDevHlpISASetIrq(pThis->CTX_SUFF(pDevIns), pThis->irq, PDM_IRQ_LEVEL_LOW);
257 pThis->port_c &= ~irq_bit;
258 }
259 else
260 {
261 pThis->port_c |= irq_bit;
262 if (!(pThis->port_c & BMS_CTL_INT_DIS))
263 PDMDevHlpISASetIrq(pThis->CTX_SUFF(pDevIns), pThis->irq, PDM_IRQ_LEVEL_HIGH);
264 }
265
266 /* Handle enabling/disabling of the mouse interface. */
267 if (pThis->port_c & BMS_CTL_INT_DIS)
268 {
269 if (pThis->disable_counter)
270 --pThis->disable_counter;
271
272 if (pThis->disable_counter == 0 && pThis->mouse_enabled)
273 {
274 pThis->mouse_enabled = false;
275 bms_update_downstream_status(pThis);
276 }
277 }
278 else
279 {
280 pThis->disable_counter = 8; /* Re-arm the disable countdown. */
281 if (!pThis->mouse_enabled)
282 {
283 pThis->mouse_enabled = true;
284 bms_update_downstream_status(pThis);
285 }
286 }
287
288 /* Re-arm the timer. */
289 TMTimerSetMillies(pTimer, pThis->cTimerPeriodMs);
290}
291
292# endif /* IN_RING3 */
293
294static void bms_set_reported_buttons(MouState *pThis, unsigned fButtons, unsigned fButtonMask)
295{
296 pThis->mouse_buttons_reported |= (fButtons & fButtonMask);
297 pThis->mouse_buttons_reported &= (fButtons | ~fButtonMask);
298}
299
300/* Update the internal state after a write to port C. */
301static void bms_update_ctrl(MouState *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 bms_set_reported_buttons(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(pThis->CTX_SUFF(pDevIns), pThis->irq, PDM_IRQ_LEVEL_LOW);
353 pThis->port_c &= ~(BMS_IRQ_BIT(pThis->irq));
354 }
355}
356
357static int bms_write_port(MouState *pThis, uint32_t offPort, uint32_t uValue)
358{
359 int rc = VINF_SUCCESS;
360
361 LogRel3(("%s: write port %d: 0x%02x\n", __PRETTY_FUNCTION__, offPort, uValue));
362
363 switch (offPort)
364 {
365 case BMS_PORT_SIG:
366 /* Update port B. */
367 pThis->port_b = uValue;
368 break;
369 case BMS_PORT_DATA:
370 /* Do nothing, port A is not writable. */
371 break;
372 case BMS_PORT_INIT:
373 pThis->ctrl_port = uValue;
374 break;
375 case BMS_PORT_CTRL:
376 /* Update the high nibble of port C. */
377 pThis->port_c = (uValue & 0xF0) | (pThis->port_c & 0x0F);
378 bms_update_ctrl(pThis);
379 break;
380 default:
381 AssertMsgFailed(("invalid port %#x\n", offPort));
382 break;
383 }
384 return rc;
385}
386
387static uint32_t bms_read_port(MouState *pThis, uint32_t offPort)
388{
389 uint32_t uValue;
390
391 switch (offPort)
392 {
393 case BMS_PORT_DATA:
394 /* Read port A. */
395 uValue = pThis->port_a;
396 break;
397 case BMS_PORT_SIG:
398 /* Read port B. */
399 uValue = pThis->port_b;
400 break;
401 case BMS_PORT_CTRL:
402 /* Read port C. */
403 uValue = pThis->port_c;
404 /* Some Microsoft driver code reads the control port 10,000 times when
405 * determining the IRQ level. This can occur faster than the IRQ line
406 * transitions and the detection fails. To work around this, we force
407 * the IRQ bit to toggle every once in a while.
408 */
409 if (pThis->irq_toggle_counter)
410 pThis->irq_toggle_counter--;
411 else
412 {
413 pThis->irq_toggle_counter = 1000;
414 uValue ^= BMS_IRQ_BIT(pThis->irq);
415 }
416 break;
417 case BMS_PORT_INIT:
418 /* Read the 8255A control port. */
419 uValue = pThis->ctrl_port;
420 break;
421 default:
422 AssertMsgFailed(("invalid port %#x\n", offPort));
423 uValue = 0xff;
424 break;
425 }
426 LogRel3(("%s: read port %d: 0x%02x\n", __PRETTY_FUNCTION__, offPort, uValue));
427 return uValue;
428}
429
430/**
431 * Port I/O Handler for port IN operations.
432 *
433 * @returns VBox status code.
434 *
435 * @param pDevIns The device instance.
436 * @param pvUser User argument - ignored.
437 * @param Port Port number used for the IN operation.
438 * @param pu32 Where to store the result.
439 * @param cb Number of bytes read.
440 */
441PDMBOTHCBDECL(int) mouIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
442{
443 NOREF(pvUser);
444 if (cb == 1)
445 {
446 MouState *pThis = PDMINS_2_DATA(pDevIns, MouState *);
447 *pu32 = bms_read_port(pThis, Port & 3);
448 Log2(("mouIOPortRead: Port=%#x cb=%d *pu32=%#x\n", Port, cb, *pu32));
449 return VINF_SUCCESS;
450 }
451 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
452 return VERR_IOM_IOPORT_UNUSED;
453}
454
455/**
456 * Port I/O Handler for port OUT operations.
457 *
458 * @returns VBox status code.
459 *
460 * @param pDevIns The device instance.
461 * @param pvUser User argument - ignored.
462 * @param Port Port number used for the IN operation.
463 * @param u32 The value to output.
464 * @param cb The value size in bytes.
465 */
466PDMBOTHCBDECL(int) mouIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
467{
468 int rc = VINF_SUCCESS;
469 NOREF(pvUser);
470 if (cb == 1)
471 {
472 MouState *pThis = PDMINS_2_DATA(pDevIns, MouState *);
473 rc = bms_write_port(pThis, Port & 3, u32);
474 Log2(("mouIOPortWrite: Port=%#x cb=%d u32=%#x\n", Port, cb, u32));
475 }
476 else
477 AssertMsgFailed(("Port=%#x cb=%d\n", Port, cb));
478 return rc;
479}
480
481# ifdef IN_RING3
482
483/**
484 * Saves the state of the device.
485 *
486 * @returns VBox status code.
487 * @param pDevIns The device instance.
488 * @param pSSMHandle The handle to save the state to.
489 */
490static DECLCALLBACK(int) mouSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
491{
492 MouState *pThis = PDMINS_2_DATA(pDevIns, MouState *);
493
494 /* 8255A state. */
495 SSMR3PutU8(pSSMHandle, pThis->port_a);
496 SSMR3PutU8(pSSMHandle, pThis->port_b);
497 SSMR3PutU8(pSSMHandle, pThis->port_c);
498 SSMR3PutU8(pSSMHandle, pThis->ctrl_port);
499 /* Other device state. */
500 SSMR3PutU8(pSSMHandle, pThis->cnt_held);
501 SSMR3PutU8(pSSMHandle, pThis->held_dx);
502 SSMR3PutU8(pSSMHandle, pThis->held_dy);
503 SSMR3PutU8(pSSMHandle, pThis->irq);
504 SSMR3PutU32(pSSMHandle, pThis->cTimerPeriodMs);
505 /* Current mouse state deltas. */
506 SSMR3PutS32(pSSMHandle, pThis->mouse_dx);
507 SSMR3PutS32(pSSMHandle, pThis->mouse_dy);
508 SSMR3PutU8(pSSMHandle, pThis->mouse_buttons_reported);
509 /* Timer. */
510 return TMR3TimerSave(pThis->MouseTimer, pSSMHandle);
511}
512
513/**
514 * Loads a saved device state.
515 *
516 * @returns VBox status code.
517 * @param pDevIns The device instance.
518 * @param pSSMHandle The handle to the saved state.
519 * @param uVersion The data unit version number.
520 * @param uPass The data pass.
521 */
522static DECLCALLBACK(int) mouLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t uVersion, uint32_t uPass)
523{
524 int rc;
525 MouState *pThis = PDMINS_2_DATA(pDevIns, MouState *);
526
527 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
528
529 if (uVersion > BMS_SAVED_STATE_VERSION)
530 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
531
532 /* 8255A state. */
533 SSMR3GetU8(pSSMHandle, &pThis->port_a);
534 SSMR3GetU8(pSSMHandle, &pThis->port_b);
535 SSMR3GetU8(pSSMHandle, &pThis->port_c);
536 SSMR3GetU8(pSSMHandle, &pThis->ctrl_port);
537 /* Other device state. */
538 SSMR3GetU8(pSSMHandle, &pThis->cnt_held);
539 SSMR3GetU8(pSSMHandle, &pThis->held_dx);
540 SSMR3GetU8(pSSMHandle, &pThis->held_dy);
541 SSMR3GetU8(pSSMHandle, &pThis->irq);
542 SSMR3GetU32(pSSMHandle, &pThis->cTimerPeriodMs);
543 /* Current mouse state deltas. */
544 SSMR3GetS32(pSSMHandle, &pThis->mouse_dx);
545 SSMR3GetS32(pSSMHandle, &pThis->mouse_dy);
546 SSMR3GetU8(pSSMHandle, &pThis->mouse_buttons_reported);
547 /* Timer. */
548 rc = TMR3TimerLoad(pThis->MouseTimer, pSSMHandle);
549 return rc;
550}
551
552/**
553 * Reset notification.
554 *
555 * @returns VBox status code.
556 * @param pDevIns The device instance data.
557 */
558static DECLCALLBACK(void) mouReset(PPDMDEVINS pDevIns)
559{
560 MouState *pThis = PDMINS_2_DATA(pDevIns, MouState *);
561
562 /* Reinitialize the timer. */
563 pThis->cTimerPeriodMs = BMS_IRQ_PERIOD_MS / 2;
564 TMTimerSetMillies(pThis->MouseTimer, pThis->cTimerPeriodMs);
565
566 bms_reset(pThis);
567}
568
569
570/* -=-=-=-=-=- Mouse: IBase -=-=-=-=-=- */
571
572/**
573 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
574 */
575static DECLCALLBACK(void *) mouQueryMouseInterface(PPDMIBASE pInterface, const char *pszIID)
576{
577 MouState *pThis = RT_FROM_MEMBER(pInterface, MouState, Mouse.IBase);
578 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->Mouse.IBase);
579 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUSEPORT, &pThis->Mouse.IPort);
580 return NULL;
581}
582
583
584/* -=-=-=-=-=- Mouse: IMousePort -=-=-=-=-=- */
585
586/**
587 * @interface_method_impl{PDMIMOUSEPORT,pfnPutEvent}
588 */
589static DECLCALLBACK(int) mouPutEvent(PPDMIMOUSEPORT pInterface, int32_t dx,
590 int32_t dy, int32_t dz, int32_t dw,
591 uint32_t fButtons)
592{
593 MouState *pThis = RT_FROM_MEMBER(pInterface, MouState, Mouse.IPort);
594 int rc = PDMCritSectEnter(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo), VERR_SEM_BUSY);
595 AssertReleaseRC(rc);
596
597 bms_mouse_event(pThis, dx, dy, dz, dw, fButtons);
598
599 PDMCritSectLeave(pThis->CTX_SUFF(pDevIns)->CTX_SUFF(pCritSectRo));
600 return VINF_SUCCESS;
601}
602
603/**
604 * @interface_method_impl{PDMIMOUSEPORT,pfnPutEventAbs}
605 */
606static DECLCALLBACK(int) mouPutEventAbs(PPDMIMOUSEPORT pInterface, uint32_t x, uint32_t y,
607 int32_t dz, int32_t dw, uint32_t fButtons)
608{
609 RT_NOREF(pInterface, x, y, dz, dw, fButtons);
610 AssertFailedReturn(VERR_NOT_SUPPORTED);
611}
612
613/**
614 * @interface_method_impl{PDMIMOUSEPORT,pfnPutEventMultiTouch}
615 */
616static DECLCALLBACK(int) mouPutEventMultiTouch(PPDMIMOUSEPORT pInterface, uint8_t cContacts,
617 const uint64_t *pau64Contacts, uint32_t u32ScanTime)
618{
619 RT_NOREF(pInterface, cContacts, pau64Contacts, u32ScanTime);
620 AssertFailedReturn(VERR_NOT_SUPPORTED);
621}
622
623/* -=-=-=-=-=- setup code -=-=-=-=-=- */
624
625
626/**
627 * Attach command.
628 *
629 * This is called to let the device attach to a driver for a specified LUN
630 * during runtime. This is not called during VM construction, the device
631 * constructor have to attach to all the available drivers.
632 *
633 * This is like plugging in the mouse after turning on the PC.
634 *
635 * @returns VBox status code.
636 * @param pDevIns The device instance.
637 * @param iLUN The logical unit which is being detached.
638 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
639 * @remark The controller doesn't support this action, this is just
640 * implemented to try out the driver<->device structure.
641 */
642static DECLCALLBACK(int) mouAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
643{
644 int rc;
645 MouState *pThis = PDMINS_2_DATA(pDevIns, MouState *);
646
647 AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
648 ("Bus mouse device does not support hotplugging\n"),
649 VERR_INVALID_PARAMETER);
650
651 switch (iLUN)
652 {
653 /* LUN #0: mouse */
654 case 0:
655 rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->Mouse.IBase, &pThis->Mouse.pDrvBase, "Bus Mouse Port");
656 if (RT_SUCCESS(rc))
657 {
658 pThis->Mouse.pDrv = PDMIBASE_QUERY_INTERFACE(pThis->Mouse.pDrvBase, PDMIMOUSECONNECTOR);
659 if (!pThis->Mouse.pDrv)
660 {
661 AssertLogRelMsgFailed(("LUN #0 doesn't have a mouse interface! rc=%Rrc\n", rc));
662 rc = VERR_PDM_MISSING_INTERFACE;
663 }
664 }
665 else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
666 {
667 Log(("%s/%d: warning: no driver attached to LUN #0!\n", pDevIns->pReg->szName, pDevIns->iInstance));
668 rc = VINF_SUCCESS;
669 }
670 else
671 AssertLogRelMsgFailed(("Failed to attach LUN #0! rc=%Rrc\n", rc));
672 break;
673
674 default:
675 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
676 return VERR_PDM_NO_SUCH_LUN;
677 }
678
679 return rc;
680}
681
682
683/**
684 * Detach notification.
685 *
686 * This is called when a driver is detaching itself from a LUN of the device.
687 * The device should adjust it's state to reflect this.
688 *
689 * This is like unplugging the network cable to use it for the laptop or
690 * something while the PC is still running.
691 *
692 * @param pDevIns The device instance.
693 * @param iLUN The logical unit which is being detached.
694 * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
695 * @remark The controller doesn't support this action, this is just
696 * implemented to try out the driver<->device structure.
697 */
698static DECLCALLBACK(void) mouDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
699{
700#if 0
701 /*
702 * Reset the interfaces and update the controller state.
703 */
704 MouState *pThis = PDMINS_2_DATA(pDevIns, MouState *);
705 switch (iLUN)
706 {
707 /* LUN #0: mouse */
708 case 0:
709 pThis->Mouse.pDrv = NULL;
710 pThis->Mouse.pDrvBase = NULL;
711 break;
712
713 default:
714 AssertMsgFailed(("Invalid LUN #%d\n", iLUN));
715 break;
716 }
717#else
718 RT_NOREF(pDevIns, iLUN, fFlags);
719#endif
720}
721
722
723/**
724 * @copydoc FNPDMDEVRELOCATE
725 */
726static DECLCALLBACK(void) mouRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
727{
728 RT_NOREF(offDelta);
729 MouState *pThis = PDMINS_2_DATA(pDevIns, MouState *);
730 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
731}
732
733
734/**
735 * @interface_method_impl{PDMDEVREG,pfnConstruct}
736 */
737static DECLCALLBACK(int) mouConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
738{
739 RT_NOREF(iInstance);
740 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
741 MouState *pThis = PDMINS_2_DATA(pDevIns, MouState *);
742 int rc;
743 bool fGCEnabled;
744 bool fR0Enabled;
745 uint8_t irq_lvl;
746 Assert(iInstance == 0);
747
748
749 /*
750 * Validate and read the configuration.
751 */
752 if (!CFGMR3AreValuesValid(pCfg, "IRQ\0GCEnabled\0R0Enabled\0"))
753 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
754 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
755 if (RT_FAILURE(rc))
756 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"GCEnabled\" from the config"));
757 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
758 if (RT_FAILURE(rc))
759 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"R0Enabled\" from the config"));
760 rc = CFGMR3QueryU8Def(pCfg, "IRQ", &irq_lvl, BMS_DEFAULT_IRQ);
761 if (RT_FAILURE(rc))
762 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"IRQ\" from the config"));
763 if ((irq_lvl < 2) || (irq_lvl > 5))
764 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Invalid \"IRQ\" config setting"));
765
766 pThis->irq = irq_lvl;
767 /// @todo remove after properly enabling RC/GC support
768 fGCEnabled = fR0Enabled = false;
769 Log(("busmouse: IRQ=%d fGCEnabled=%RTbool fR0Enabled=%RTbool\n", irq_lvl, fGCEnabled, fR0Enabled));
770
771 /*
772 * Initialize the interfaces.
773 */
774 pThis->pDevInsR3 = pDevIns;
775 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
776 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
777 pThis->Mouse.IBase.pfnQueryInterface = mouQueryMouseInterface;
778 pThis->Mouse.IPort.pfnPutEvent = mouPutEvent;
779 pThis->Mouse.IPort.pfnPutEventAbs = mouPutEventAbs;
780 pThis->Mouse.IPort.pfnPutEventMultiTouch = mouPutEventMultiTouch;
781
782 /*
783 * Create the interrupt timer.
784 */
785 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, bmsTimerCallback,
786 pThis, TMTIMER_FLAGS_DEFAULT_CRIT_SECT,
787 "Bus Mouse Timer", &pThis->MouseTimer);
788 if (RT_FAILURE(rc))
789 return rc;
790
791 /*
792 * Register I/O ports, saved state, and mouse event handlers.
793 */
794 rc = PDMDevHlpIOPortRegister(pDevIns, BMS_IO_BASE, BMS_IO_SIZE, NULL, mouIOPortWrite, mouIOPortRead, NULL, NULL, "Bus Mouse");
795 if (RT_FAILURE(rc))
796 return rc;
797 if (fGCEnabled)
798 {
799 rc = PDMDevHlpIOPortRegisterRC(pDevIns, BMS_IO_BASE, BMS_IO_SIZE, 0, "mouIOPortWrite", "mouIOPortRead", NULL, NULL, "Bus Mouse");
800 if (RT_FAILURE(rc))
801 return rc;
802 }
803 if (fR0Enabled)
804 {
805 rc = PDMDevHlpIOPortRegisterR0(pDevIns, BMS_IO_BASE, BMS_IO_SIZE, 0, "mouIOPortWrite", "mouIOPortRead", NULL, NULL, "Bus Mouse");
806 if (RT_FAILURE(rc))
807 return rc;
808 }
809 rc = PDMDevHlpSSMRegister(pDevIns, BMS_SAVED_STATE_VERSION, sizeof(*pThis), mouSaveExec, mouLoadExec);
810 if (RT_FAILURE(rc))
811 return rc;
812
813 /*
814 * Attach to the mouse driver.
815 */
816 rc = mouAttach(pDevIns, 0, PDM_TACH_FLAGS_NOT_HOT_PLUG);
817 if (RT_FAILURE(rc))
818 return rc;
819
820 /*
821 * Initialize the device state.
822 */
823 mouReset(pDevIns);
824
825 return VINF_SUCCESS;
826}
827
828
829/**
830 * The device registration structure.
831 */
832const PDMDEVREG g_DeviceBusMouse =
833{
834 /* u32Version */
835 PDM_DEVREG_VERSION,
836 /* szName */
837 "busmouse",
838 /* szRCMod */
839 "VBoxDDRC.rc",
840 /* szR0Mod */
841 "VBoxDDR0.r0",
842 /* pszDescription */
843 "Microsoft Bus Mouse controller. "
844 "LUN #0 is the mouse connector.",
845 /* fFlags */
846 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64 | PDM_DEVREG_FLAGS_PAE36
847 | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
848 /* fClass */
849 PDM_DEVREG_CLASS_INPUT,
850 /* cMaxInstances */
851 1,
852 /* cbInstance */
853 sizeof(MouState),
854 /* pfnConstruct */
855 mouConstruct,
856 /* pfnDestruct */
857 NULL,
858 /* pfnRelocate */
859 mouRelocate,
860 /* pfnMemSetup */
861 NULL,
862 /* pfnPowerOn */
863 NULL,
864 /* pfnReset */
865 mouReset,
866 /* pfnSuspend */
867 NULL,
868 /* pfnResume */
869 NULL,
870 /* pfnAttach */
871 mouAttach,
872 /* pfnDetach */
873 mouDetach,
874 /* pfnQueryInterface. */
875 NULL,
876 /* pfnInitComplete */
877 NULL,
878 /* pfnPowerOff */
879 NULL,
880 /* pfnSoftReset */
881 NULL,
882 /* u32VersionEnd */
883 PDM_DEVREG_VERSION
884};
885
886#ifdef VBOX_IN_EXTPACK_R3
887/**
888 * @callback_method_impl{FNPDMVBOXDEVICESREGISTER}
889 */
890extern "C" DECLEXPORT(int) VBoxDevicesRegister(PPDMDEVREGCB pCallbacks, uint32_t u32Version)
891{
892 AssertLogRelMsgReturn(u32Version >= VBOX_VERSION,
893 ("u32Version=%#x VBOX_VERSION=%#x\n", u32Version, VBOX_VERSION),
894 VERR_EXTPACK_VBOX_VERSION_MISMATCH);
895 AssertLogRelMsgReturn(pCallbacks->u32Version == PDM_DEVREG_CB_VERSION,
896 ("pCallbacks->u32Version=%#x PDM_DEVREG_CB_VERSION=%#x\n", pCallbacks->u32Version, PDM_DEVREG_CB_VERSION),
897 VERR_VERSION_MISMATCH);
898
899 return pCallbacks->pfnRegister(pCallbacks, &g_DeviceBusMouse);
900}
901#endif /* VBOX_IN_EXTPACK_R3 */
902
903# endif /* IN_RING3 */
904#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
905
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