VirtualBox

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

Last change on this file since 35682 was 35682, checked in by vboxsync, 14 years ago

Added a sample device (bus mouse).

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