VirtualBox

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

Last change on this file since 44446 was 44446, checked in by vboxsync, 12 years ago

Made the bus mouse sample extpack buildable. Cleaned it up a little bit, eliminating an unncessary crit sect.

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

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