VirtualBox

source: vbox/trunk/src/VBox/Main/SerialPortImpl.cpp@ 25275

Last change on this file since 25275 was 25203, checked in by vboxsync, 15 years ago

Main: make SerialPort instance data private and make it use the XML settings struct for simplicity

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

© 2023 Oracle
ContactPrivacy policyTerms of Use