VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/SerialPortImpl.cpp@ 37989

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

Main. QT/FE: fix long standing COM issue

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.2 KB
Line 
1/** @file
2 *
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2007 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include "SerialPortImpl.h"
19#include "MachineImpl.h"
20#include "VirtualBoxImpl.h"
21#include "GuestOSTypeImpl.h"
22
23#include <iprt/string.h>
24#include <iprt/cpp/utils.h>
25
26#include <VBox/settings.h>
27
28#include "AutoStateDep.h"
29#include "AutoCaller.h"
30#include "Logging.h"
31
32////////////////////////////////////////////////////////////////////////////////
33//
34// SerialPort private data definition
35//
36////////////////////////////////////////////////////////////////////////////////
37
38struct SerialPort::Data
39{
40 Data()
41 : fModified(false),
42 pMachine(NULL)
43 { }
44
45 bool fModified;
46
47 Machine * const pMachine;
48 const ComObjPtr<SerialPort> pPeer;
49
50 Backupable<settings::SerialPort> bd;
51};
52
53// constructor / destructor
54/////////////////////////////////////////////////////////////////////////////
55
56DEFINE_EMPTY_CTOR_DTOR (SerialPort)
57
58HRESULT SerialPort::FinalConstruct()
59{
60 return BaseFinalConstruct();
61}
62
63void SerialPort::FinalRelease()
64{
65 uninit();
66 BaseFinalRelease();
67}
68
69// public initializer/uninitializer for internal purposes only
70/////////////////////////////////////////////////////////////////////////////
71
72/**
73 * Initializes the Serial Port object.
74 *
75 * @param aParent Handle of the parent object.
76 */
77HRESULT SerialPort::init(Machine *aParent, ULONG aSlot)
78{
79 LogFlowThisFunc(("aParent=%p, aSlot=%d\n", aParent, aSlot));
80
81 ComAssertRet(aParent, E_INVALIDARG);
82
83 /* Enclose the state transition NotReady->InInit->Ready */
84 AutoInitSpan autoInitSpan(this);
85 AssertReturn(autoInitSpan.isOk(), E_FAIL);
86
87 m = new Data();
88
89 unconst(m->pMachine) = aParent;
90 /* m->pPeer is left null */
91
92 m->bd.allocate();
93
94 /* initialize data */
95 m->bd->ulSlot = aSlot;
96
97 /* Confirm a successful initialization */
98 autoInitSpan.setSucceeded();
99
100 return S_OK;
101}
102
103/**
104 * Initializes the Serial Port object given another serial port object
105 * (a kind of copy constructor). This object shares data with
106 * the object passed as an argument.
107 *
108 * @note This object must be destroyed before the original object
109 * it shares data with is destroyed.
110 *
111 * @note Locks @a aThat object for reading.
112 */
113HRESULT SerialPort::init(Machine *aParent, SerialPort *aThat)
114{
115 LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat));
116
117 ComAssertRet(aParent && aThat, E_INVALIDARG);
118
119 /* Enclose the state transition NotReady->InInit->Ready */
120 AutoInitSpan autoInitSpan(this);
121 AssertReturn(autoInitSpan.isOk(), E_FAIL);
122
123 m = new Data();
124
125 unconst(m->pMachine) = aParent;
126 unconst(m->pPeer) = aThat;
127
128 AutoCaller thatCaller (aThat);
129 AssertComRCReturnRC(thatCaller.rc());
130
131 AutoReadLock thatLock(aThat COMMA_LOCKVAL_SRC_POS);
132 m->bd.share (aThat->m->bd);
133
134 /* Confirm a successful initialization */
135 autoInitSpan.setSucceeded();
136
137 return S_OK;
138}
139
140/**
141 * Initializes the guest object given another guest object
142 * (a kind of copy constructor). This object makes a private copy of data
143 * of the original object passed as an argument.
144 *
145 * @note Locks @a aThat object for reading.
146 */
147HRESULT SerialPort::initCopy(Machine *aParent, SerialPort *aThat)
148{
149 LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat));
150
151 ComAssertRet(aParent && aThat, E_INVALIDARG);
152
153 /* Enclose the state transition NotReady->InInit->Ready */
154 AutoInitSpan autoInitSpan(this);
155 AssertReturn(autoInitSpan.isOk(), E_FAIL);
156
157 m = new Data();
158
159 unconst(m->pMachine) = aParent;
160 /* pPeer is left null */
161
162 AutoCaller thatCaller (aThat);
163 AssertComRCReturnRC(thatCaller.rc());
164
165 AutoReadLock thatLock(aThat COMMA_LOCKVAL_SRC_POS);
166 m->bd.attachCopy (aThat->m->bd);
167
168 /* Confirm a successful initialization */
169 autoInitSpan.setSucceeded();
170
171 return S_OK;
172}
173
174/**
175 * Uninitializes the instance and sets the ready flag to FALSE.
176 * Called either from FinalRelease() or by the parent when it gets destroyed.
177 */
178void SerialPort::uninit()
179{
180 LogFlowThisFunc(("\n"));
181
182 /* Enclose the state transition Ready->InUninit->NotReady */
183 AutoUninitSpan autoUninitSpan(this);
184 if (autoUninitSpan.uninitDone())
185 return;
186
187 m->bd.free();
188
189 unconst(m->pPeer) = NULL;
190 unconst(m->pMachine) = NULL;
191
192 delete m;
193 m = NULL;
194}
195
196// ISerialPort properties
197/////////////////////////////////////////////////////////////////////////////
198
199STDMETHODIMP SerialPort::COMGETTER(Enabled) (BOOL *aEnabled)
200{
201 CheckComArgOutPointerValid(aEnabled);
202
203 AutoCaller autoCaller(this);
204 if (FAILED(autoCaller.rc())) return autoCaller.rc();
205
206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
207
208 *aEnabled = m->bd->fEnabled;
209
210 return S_OK;
211}
212
213STDMETHODIMP SerialPort::COMSETTER(Enabled) (BOOL aEnabled)
214{
215 LogFlowThisFunc(("aEnabled=%RTbool\n", aEnabled));
216
217 AutoCaller autoCaller(this);
218 if (FAILED(autoCaller.rc())) return autoCaller.rc();
219
220 /* the machine needs to be mutable */
221 AutoMutableStateDependency adep(m->pMachine);
222 if (FAILED(adep.rc())) return adep.rc();
223
224 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
225
226 if (m->bd->fEnabled != !!aEnabled)
227 {
228 m->bd.backup();
229 m->bd->fEnabled = !!aEnabled;
230
231 m->fModified = true;
232 // leave the lock before informing callbacks
233 alock.release();
234
235 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
236 m->pMachine->setModified(Machine::IsModified_SerialPorts);
237 mlock.release();
238
239 m->pMachine->onSerialPortChange(this);
240 }
241
242 return S_OK;
243}
244
245STDMETHODIMP SerialPort::COMGETTER(HostMode) (PortMode_T *aHostMode)
246{
247 CheckComArgOutPointerValid(aHostMode);
248
249 AutoCaller autoCaller(this);
250 if (FAILED(autoCaller.rc())) return autoCaller.rc();
251
252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
253
254 *aHostMode = m->bd->portMode;
255
256 return S_OK;
257}
258
259STDMETHODIMP SerialPort::COMSETTER(HostMode) (PortMode_T aHostMode)
260{
261 AutoCaller autoCaller(this);
262 if (FAILED(autoCaller.rc())) return autoCaller.rc();
263
264 /* the machine needs to be mutable */
265 AutoMutableStateDependency adep(m->pMachine);
266 if (FAILED(adep.rc())) return adep.rc();
267
268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
269
270 if (m->bd->portMode != aHostMode)
271 {
272 switch (aHostMode)
273 {
274 case PortMode_RawFile:
275 if (m->bd->strPath.isEmpty())
276 return setError(E_INVALIDARG,
277 tr("Cannot set the raw file mode of the serial port %d "
278 "because the file path is empty or null"),
279 m->bd->ulSlot);
280 break;
281 case PortMode_HostPipe:
282 if (m->bd->strPath.isEmpty())
283 return setError(E_INVALIDARG,
284 tr("Cannot set the host pipe mode of the serial port %d "
285 "because the pipe path is empty or null"),
286 m->bd->ulSlot);
287 break;
288 case PortMode_HostDevice:
289 if (m->bd->strPath.isEmpty())
290 return setError(E_INVALIDARG,
291 tr("Cannot set the host device mode of the serial port %d "
292 "because the device path is empty or null"),
293 m->bd->ulSlot);
294 break;
295 case PortMode_Disconnected:
296 break;
297 }
298
299 m->bd.backup();
300 m->bd->portMode = aHostMode;
301
302 m->fModified = true;
303 // leave the lock before informing callbacks
304 alock.release();
305
306 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
307 m->pMachine->setModified(Machine::IsModified_SerialPorts);
308 mlock.release();
309
310 m->pMachine->onSerialPortChange(this);
311 }
312
313 return S_OK;
314}
315
316STDMETHODIMP SerialPort::COMGETTER(Slot) (ULONG *aSlot)
317{
318 CheckComArgOutPointerValid(aSlot);
319
320 AutoCaller autoCaller(this);
321 if (FAILED(autoCaller.rc())) return autoCaller.rc();
322
323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
324
325 *aSlot = m->bd->ulSlot;
326
327 return S_OK;
328}
329
330STDMETHODIMP SerialPort::COMGETTER(IRQ) (ULONG *aIRQ)
331{
332 CheckComArgOutPointerValid(aIRQ);
333
334 AutoCaller autoCaller(this);
335 if (FAILED(autoCaller.rc())) return autoCaller.rc();
336
337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
338
339 *aIRQ = m->bd->ulIRQ;
340
341 return S_OK;
342}
343
344STDMETHODIMP SerialPort::COMSETTER(IRQ)(ULONG aIRQ)
345{
346 /* check IRQ limits
347 * (when changing this, make sure it corresponds to XML schema */
348 if (aIRQ > 255)
349 return setError(E_INVALIDARG,
350 tr("Invalid IRQ number of the serial port %d: %lu (must be in range [0, %lu])"),
351 m->bd->ulSlot, aIRQ, 255);
352
353 AutoCaller autoCaller(this);
354 if (FAILED(autoCaller.rc())) return autoCaller.rc();
355
356 /* the machine needs to be mutable */
357 AutoMutableStateDependency adep(m->pMachine);
358 if (FAILED(adep.rc())) return adep.rc();
359
360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
361
362 if (m->bd->ulIRQ != aIRQ)
363 {
364 m->bd.backup();
365 m->bd->ulIRQ = aIRQ;
366
367 m->fModified = true;
368 // leave the lock before informing callbacks
369 alock.release();
370
371 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
372 m->pMachine->setModified(Machine::IsModified_SerialPorts);
373 mlock.release();
374
375 m->pMachine->onSerialPortChange(this);
376 }
377
378 return S_OK;
379}
380
381STDMETHODIMP SerialPort::COMGETTER(IOBase) (ULONG *aIOBase)
382{
383 CheckComArgOutPointerValid(aIOBase);
384
385 AutoCaller autoCaller(this);
386 if (FAILED(autoCaller.rc())) return autoCaller.rc();
387
388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
389
390 *aIOBase = m->bd->ulIOBase;
391
392 return S_OK;
393}
394
395STDMETHODIMP SerialPort::COMSETTER(IOBase)(ULONG aIOBase)
396{
397 /* check IOBase limits
398 * (when changing this, make sure it corresponds to XML schema */
399 if (aIOBase > 0xFFFF)
400 return setError(E_INVALIDARG,
401 tr("Invalid I/O port base address of the serial port %d: %lu (must be in range [0, 0x%X])"),
402 m->bd->ulSlot, aIOBase, 0, 0xFFFF);
403
404 AutoCaller autoCaller(this);
405 if (FAILED(autoCaller.rc())) return autoCaller.rc();
406
407 /* the machine needs to be mutable */
408 AutoMutableStateDependency adep(m->pMachine);
409 if (FAILED(adep.rc())) return adep.rc();
410
411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
412
413 HRESULT rc = S_OK;
414
415 if (m->bd->ulIOBase != aIOBase)
416 {
417 m->bd.backup();
418 m->bd->ulIOBase = aIOBase;
419
420 m->fModified = true;
421 // leave the lock before informing callbacks
422 alock.release();
423
424 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
425 m->pMachine->setModified(Machine::IsModified_SerialPorts);
426 mlock.release();
427
428 m->pMachine->onSerialPortChange(this);
429 }
430
431 return rc;
432}
433
434STDMETHODIMP SerialPort::COMGETTER(Path) (BSTR *aPath)
435{
436 CheckComArgOutPointerValid(aPath);
437
438 AutoCaller autoCaller(this);
439 if (FAILED(autoCaller.rc())) return autoCaller.rc();
440
441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
442
443 m->bd->strPath.cloneTo(aPath);
444
445 return S_OK;
446}
447
448STDMETHODIMP SerialPort::COMSETTER(Path) (IN_BSTR aPath)
449{
450 AutoCaller autoCaller(this);
451 if (FAILED(autoCaller.rc())) return autoCaller.rc();
452
453 /* the machine needs to be mutable */
454 AutoMutableStateDependency adep(m->pMachine);
455 if (FAILED(adep.rc())) return adep.rc();
456
457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
458
459 /* we treat empty as null when e.g. saving to XML, do the same here */
460 if (aPath && *aPath == '\0')
461 aPath = NULL;
462
463 Utf8Str str(aPath);
464 if (str != m->bd->strPath)
465 {
466 HRESULT rc = checkSetPath(str);
467 if (FAILED(rc)) return rc;
468
469 m->bd.backup();
470 m->bd->strPath = str;
471
472 m->fModified = true;
473 // leave the lock before informing callbacks
474 alock.release();
475
476 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
477 m->pMachine->setModified(Machine::IsModified_SerialPorts);
478 mlock.release();
479
480 m->pMachine->onSerialPortChange(this);
481 }
482
483 return S_OK;
484}
485
486STDMETHODIMP SerialPort::COMGETTER(Server) (BOOL *aServer)
487{
488 CheckComArgOutPointerValid(aServer);
489
490 AutoCaller autoCaller(this);
491 if (FAILED(autoCaller.rc())) return autoCaller.rc();
492
493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
494
495 *aServer = m->bd->fServer;
496
497 return S_OK;
498}
499
500STDMETHODIMP SerialPort::COMSETTER(Server) (BOOL aServer)
501{
502 AutoCaller autoCaller(this);
503 if (FAILED(autoCaller.rc())) return autoCaller.rc();
504
505 /* the machine needs to be mutable */
506 AutoMutableStateDependency adep(m->pMachine);
507 if (FAILED(adep.rc())) return adep.rc();
508
509 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
510
511 if (m->bd->fServer != !!aServer)
512 {
513 m->bd.backup();
514 m->bd->fServer = !!aServer;
515
516 m->fModified = true;
517 // leave the lock before informing callbacks
518 alock.release();
519
520 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
521 m->pMachine->setModified(Machine::IsModified_SerialPorts);
522 mlock.release();
523
524 m->pMachine->onSerialPortChange(this);
525 }
526
527 return S_OK;
528}
529
530// public methods only for internal purposes
531////////////////////////////////////////////////////////////////////////////////
532
533/**
534 * Loads settings from the given port node.
535 * May be called once right after this object creation.
536 *
537 * @param aPortNode <Port> node.
538 *
539 * @note Locks this object for writing.
540 */
541HRESULT SerialPort::loadSettings(const settings::SerialPort &data)
542{
543 AutoCaller autoCaller(this);
544 AssertComRCReturnRC(autoCaller.rc());
545
546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
547
548 // simply copy
549 *m->bd.data() = data;
550
551 return S_OK;
552}
553
554/**
555 * Saves the port settings to the given port node.
556 *
557 * Note that the given Port node is completely empty on input.
558 *
559 * @param aPortNode <Port> node.
560 *
561 * @note Locks this object for reading.
562 */
563HRESULT SerialPort::saveSettings(settings::SerialPort &data)
564{
565 AutoCaller autoCaller(this);
566 AssertComRCReturnRC(autoCaller.rc());
567
568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
569
570 // simply copy
571 data = *m->bd.data();
572
573 return S_OK;
574}
575
576/**
577 * Returns true if any setter method has modified settings of this instance.
578 * @return
579 */
580bool SerialPort::isModified()
581{
582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
583 return m->fModified;
584}
585
586/**
587 * @note Locks this object for writing.
588 */
589void SerialPort::rollback()
590{
591 /* sanity */
592 AutoCaller autoCaller(this);
593 AssertComRCReturnVoid(autoCaller.rc());
594
595 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
596
597 m->bd.rollback();
598}
599
600/**
601 * @note Locks this object for writing, together with the peer object (also
602 * for writing) if there is one.
603 */
604void SerialPort::commit()
605{
606 /* sanity */
607 AutoCaller autoCaller(this);
608 AssertComRCReturnVoid (autoCaller.rc());
609
610 /* sanity too */
611 AutoCaller peerCaller(m->pPeer);
612 AssertComRCReturnVoid(peerCaller.rc());
613
614 /* lock both for writing since we modify both (pPeer is "master" so locked
615 * first) */
616 AutoMultiWriteLock2 alock(m->pPeer, this COMMA_LOCKVAL_SRC_POS);
617
618 if (m->bd.isBackedUp())
619 {
620 m->bd.commit();
621 if (m->pPeer)
622 {
623 /* attach new data to the peer and reshare it */
624 m->pPeer->m->bd.attach(m->bd);
625 }
626 }
627}
628
629/**
630 * @note Locks this object for writing, together with the peer object
631 * represented by @a aThat (locked for reading).
632 */
633void SerialPort::copyFrom (SerialPort *aThat)
634{
635 AssertReturnVoid (aThat != NULL);
636
637 /* sanity */
638 AutoCaller autoCaller(this);
639 AssertComRCReturnVoid (autoCaller.rc());
640
641 /* sanity too */
642 AutoCaller thatCaller (aThat);
643 AssertComRCReturnVoid (thatCaller.rc());
644
645 /* peer is not modified, lock it for reading (aThat is "master" so locked
646 * first) */
647 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
648 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
649
650 /* this will back up current data */
651 m->bd.assignCopy (aThat->m->bd);
652}
653
654void SerialPort::applyDefaults (GuestOSType *aOsType)
655{
656 AssertReturnVoid (aOsType != NULL);
657
658 /* sanity */
659 AutoCaller autoCaller(this);
660 AssertComRCReturnVoid (autoCaller.rc());
661
662 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
663
664 /* Set some more defaults.
665 * Note that the default value for COM1 (slot 0) is set in the constructor
666 * of bd. So slot 0 is correct already. */
667 switch (m->bd->ulSlot)
668 {
669 case 1:
670 {
671 m->bd->ulIOBase = 0x2F8;
672 m->bd->ulIRQ = 3;
673 break;
674 }
675 case 2:
676 {
677 m->bd->ulIOBase = 0x3e8;
678 m->bd->ulIRQ = 4;
679 break;
680 }
681 case 3:
682 {
683 m->bd->ulIOBase = 0x2e8;
684 m->bd->ulIRQ = 3;
685 break;
686 }
687 default: break;
688 }
689
690 uint32_t numSerialEnabled = aOsType->numSerialEnabled();
691
692 /* Enable port if requested */
693 if (m->bd->ulSlot < numSerialEnabled)
694 {
695 m->bd->fEnabled = true;
696 }
697}
698
699/**
700 * Validates COMSETTER(Path) arguments.
701 */
702HRESULT SerialPort::checkSetPath(const Utf8Str &str)
703{
704 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
705
706 if ( ( m->bd->portMode == PortMode_HostDevice
707 || m->bd->portMode == PortMode_HostPipe
708 || m->bd->portMode == PortMode_RawFile
709 ) && str.isEmpty()
710 )
711 return setError(E_INVALIDARG,
712 tr("Path of the serial port %d may not be empty or null in "
713 "host pipe or host device mode"),
714 m->bd->ulSlot);
715
716 return S_OK;
717}
718
719/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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