VirtualBox

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

Last change on this file since 23319 was 22173, checked in by vboxsync, 15 years ago

Main: the big XML settings rework. Move XML reading/writing out of interface implementation code into separate layer so it can handle individual settings versions in the future.

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