VirtualBox

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

Last change on this file since 40280 was 40280, checked in by vboxsync, 13 years ago

Corrected a bunch of HC and GC uses in status codes.

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