VirtualBox

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

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

Main: coding style: have Main obey the standard VirtualBox coding style rules (no functional changes)

  • Property svn:eol-style set to native
File size: 18.5 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::Key &aPortNode)
178{
179 using namespace settings;
180
181 AssertReturn(!aPortNode.isNull(), E_FAIL);
182
183 AutoCaller autoCaller(this);
184 AssertComRCReturnRC(autoCaller.rc());
185
186 AutoWriteLock alock(this);
187
188 /* Note: we assume that the default values for attributes of optional
189 * nodes are assigned in the Data::Data() constructor and don't do it
190 * here. It implies that this method may only be called after constructing
191 * a new BIOSSettings object while all its data fields are in the default
192 * values. Exceptions are fields whose creation time defaults don't match
193 * values that should be applied when these fields are not explicitly set
194 * in the settings file (for backwards compatibility reasons). This takes
195 * place when a setting of a newly created object must default to A while
196 * the same setting of an object loaded from the old settings file must
197 * default to B. */
198
199 /* enabled (required) */
200 mData->mEnabled = aPortNode.value <bool> ("enabled");
201 /* I/O base (required) */
202 mData->mIOBase = aPortNode.value <ULONG> ("IOBase");
203 /* IRQ (required) */
204 mData->mIRQ = aPortNode.value <ULONG> ("IRQ");
205 /* host mode (required) */
206 const char *mode = aPortNode.stringValue ("hostMode");
207 if (strcmp (mode, "RawFile") == 0)
208 mData->mHostMode = PortMode_RawFile;
209 else if (strcmp (mode, "HostPipe") == 0)
210 mData->mHostMode = PortMode_HostPipe;
211 else if (strcmp (mode, "HostDevice") == 0)
212 mData->mHostMode = PortMode_HostDevice;
213 else if (strcmp (mode, "Disconnected") == 0)
214 mData->mHostMode = PortMode_Disconnected;
215 else
216 ComAssertMsgFailedRet (("Invalid port mode '%s'", mode), E_FAIL);
217
218 /* pipe/device path (optional, defaults to null) */
219 Bstr path = aPortNode.stringValue ("path");
220 HRESULT rc = checkSetPath (path);
221 CheckComRCReturnRC(rc);
222 mData->mPath = path;
223
224 /* server mode (optional, defaults to false) */
225 mData->mServer = aPortNode.value <bool> ("server");
226
227 return S_OK;
228}
229
230/**
231 * Saves the port settings to the given port node.
232 *
233 * Note that the given Port node is comletely empty on input.
234 *
235 * @param aPortNode <Port> node.
236 *
237 * @note Locks this object for reading.
238 */
239HRESULT SerialPort::saveSettings (settings::Key &aPortNode)
240{
241 using namespace settings;
242
243 AssertReturn(!aPortNode.isNull(), E_FAIL);
244
245 AutoCaller autoCaller(this);
246 AssertComRCReturnRC(autoCaller.rc());
247
248 AutoReadLock alock(this);
249
250 aPortNode.setValue <bool> ("enabled", !!mData->mEnabled);
251 aPortNode.setValue <ULONG> ("IOBase", mData->mIOBase, 16);
252 aPortNode.setValue <ULONG> ("IRQ", mData->mIRQ);
253
254 const char *mode = NULL;
255 switch (mData->mHostMode)
256 {
257 case PortMode_Disconnected:
258 mode = "Disconnected";
259 break;
260 case PortMode_HostPipe:
261 mode = "HostPipe";
262 break;
263 case PortMode_HostDevice:
264 mode = "HostDevice";
265 break;
266 case PortMode_RawFile:
267 mode = "RawFile";
268 break;
269 default:
270 ComAssertMsgFailedRet (("Invalid serial port mode: %d",
271 mData->mHostMode),
272 E_FAIL);
273 }
274 aPortNode.setStringValue ("hostMode", mode);
275
276 /* Always save non-null mPath and mServer to preserve the user values for
277 * later use. Note that 'server' is false by default in XML so we don't
278 * save it when it's false. */
279 if (!mData->mPath.isEmpty())
280 aPortNode.setValue <Bstr> ("path", mData->mPath);
281 if (mData->mServer)
282 aPortNode.setValue <bool> ("server", !!mData->mServer);
283
284 return S_OK;
285}
286
287/**
288 * @note Locks this object for writing.
289 */
290bool SerialPort::rollback()
291{
292 /* sanity */
293 AutoCaller autoCaller(this);
294 AssertComRCReturn (autoCaller.rc(), false);
295
296 AutoWriteLock alock(this);
297
298 bool changed = false;
299
300 if (mData.isBackedUp())
301 {
302 /* we need to check all data to see whether anything will be changed
303 * after rollback */
304 changed = mData.hasActualChanges();
305 mData.rollback();
306 }
307
308 return changed;
309}
310
311/**
312 * @note Locks this object for writing, together with the peer object (also
313 * for writing) if there is one.
314 */
315void SerialPort::commit()
316{
317 /* sanity */
318 AutoCaller autoCaller(this);
319 AssertComRCReturnVoid (autoCaller.rc());
320
321 /* sanity too */
322 AutoCaller peerCaller (mPeer);
323 AssertComRCReturnVoid (peerCaller.rc());
324
325 /* lock both for writing since we modify both (mPeer is "master" so locked
326 * first) */
327 AutoMultiWriteLock2 alock (mPeer, this);
328
329 if (mData.isBackedUp())
330 {
331 mData.commit();
332 if (mPeer)
333 {
334 /* attach new data to the peer and reshare it */
335 mPeer->mData.attach (mData);
336 }
337 }
338}
339
340/**
341 * @note Locks this object for writing, together with the peer object
342 * represented by @a aThat (locked for reading).
343 */
344void SerialPort::copyFrom (SerialPort *aThat)
345{
346 AssertReturnVoid (aThat != NULL);
347
348 /* sanity */
349 AutoCaller autoCaller(this);
350 AssertComRCReturnVoid (autoCaller.rc());
351
352 /* sanity too */
353 AutoCaller thatCaller (aThat);
354 AssertComRCReturnVoid (thatCaller.rc());
355
356 /* peer is not modified, lock it for reading (aThat is "master" so locked
357 * first) */
358 AutoMultiLock2 alock (aThat->rlock(), this->wlock());
359
360 /* this will back up current data */
361 mData.assignCopy (aThat->mData);
362}
363
364void SerialPort::applyDefaults (GuestOSType *aOsType)
365{
366 AssertReturnVoid (aOsType != NULL);
367
368 /* sanity */
369 AutoCaller autoCaller(this);
370 AssertComRCReturnVoid (autoCaller.rc());
371
372 AutoWriteLock alock(this);
373
374 uint32_t numSerialEnabled = aOsType->numSerialEnabled();
375
376 /* Enable port if requested */
377 if (mData->mSlot < numSerialEnabled)
378 {
379 mData->mEnabled = true;
380 }
381}
382
383// ISerialPort properties
384/////////////////////////////////////////////////////////////////////////////
385
386STDMETHODIMP SerialPort::COMGETTER(Enabled) (BOOL *aEnabled)
387{
388 CheckComArgOutPointerValid(aEnabled);
389
390 AutoCaller autoCaller(this);
391 CheckComRCReturnRC(autoCaller.rc());
392
393 AutoReadLock alock(this);
394
395 *aEnabled = mData->mEnabled;
396
397 return S_OK;
398}
399
400STDMETHODIMP SerialPort::COMSETTER(Enabled) (BOOL aEnabled)
401{
402 LogFlowThisFunc(("aEnabled=%RTbool\n", aEnabled));
403
404 AutoCaller autoCaller(this);
405 CheckComRCReturnRC(autoCaller.rc());
406
407 /* the machine needs to be mutable */
408 Machine::AutoMutableStateDependency adep (mParent);
409 CheckComRCReturnRC(adep.rc());
410
411 AutoWriteLock alock(this);
412
413 if (mData->mEnabled != aEnabled)
414 {
415 mData.backup();
416 mData->mEnabled = aEnabled;
417
418 /* leave the lock before informing callbacks */
419 alock.unlock();
420
421 mParent->onSerialPortChange (this);
422 }
423
424 return S_OK;
425}
426
427STDMETHODIMP SerialPort::COMGETTER(HostMode) (PortMode_T *aHostMode)
428{
429 CheckComArgOutPointerValid(aHostMode);
430
431 AutoCaller autoCaller(this);
432 CheckComRCReturnRC(autoCaller.rc());
433
434 AutoReadLock alock(this);
435
436 *aHostMode = mData->mHostMode;
437
438 return S_OK;
439}
440
441STDMETHODIMP SerialPort::COMSETTER(HostMode) (PortMode_T aHostMode)
442{
443 AutoCaller autoCaller(this);
444 CheckComRCReturnRC(autoCaller.rc());
445
446 /* the machine needs to be mutable */
447 Machine::AutoMutableStateDependency adep (mParent);
448 CheckComRCReturnRC(adep.rc());
449
450 AutoWriteLock alock(this);
451
452 HRESULT rc = S_OK;
453 bool emitChangeEvent = false;
454
455 if (mData->mHostMode != aHostMode)
456 {
457 switch (aHostMode)
458 {
459 case PortMode_RawFile:
460 if (mData->mPath.isEmpty())
461 return setError (E_INVALIDARG,
462 tr ("Cannot set the raw file mode of the serial port %d "
463 "because the file path is empty or null"),
464 mData->mSlot);
465 break;
466 case PortMode_HostPipe:
467 if (mData->mPath.isEmpty())
468 return setError (E_INVALIDARG,
469 tr ("Cannot set the host pipe mode of the serial port %d "
470 "because the pipe path is empty or null"),
471 mData->mSlot);
472 break;
473 case PortMode_HostDevice:
474 if (mData->mPath.isEmpty())
475 return setError (E_INVALIDARG,
476 tr ("Cannot set the host device mode of the serial port %d "
477 "because the device path is empty or null"),
478 mData->mSlot);
479 break;
480 case PortMode_Disconnected:
481 break;
482 }
483
484 mData.backup();
485 mData->mHostMode = aHostMode;
486
487 emitChangeEvent = true;
488 }
489
490 if (emitChangeEvent)
491 {
492 /* leave the lock before informing callbacks */
493 alock.unlock();
494
495 mParent->onSerialPortChange (this);
496 }
497
498 return rc;
499}
500
501STDMETHODIMP SerialPort::COMGETTER(Slot) (ULONG *aSlot)
502{
503 CheckComArgOutPointerValid(aSlot);
504
505 AutoCaller autoCaller(this);
506 CheckComRCReturnRC(autoCaller.rc());
507
508 AutoReadLock alock(this);
509
510 *aSlot = mData->mSlot;
511
512 return S_OK;
513}
514
515STDMETHODIMP SerialPort::COMGETTER(IRQ) (ULONG *aIRQ)
516{
517 CheckComArgOutPointerValid(aIRQ);
518
519 AutoCaller autoCaller(this);
520 CheckComRCReturnRC(autoCaller.rc());
521
522 AutoReadLock alock(this);
523
524 *aIRQ = mData->mIRQ;
525
526 return S_OK;
527}
528
529STDMETHODIMP SerialPort::COMSETTER(IRQ)(ULONG aIRQ)
530{
531 /* check IRQ limits
532 * (when changing this, make sure it corresponds to XML schema */
533 if (aIRQ > 255)
534 return setError (E_INVALIDARG,
535 tr ("Invalid IRQ number of the serial port %d: "
536 "%lu (must be in range [0, %lu])"),
537 mData->mSlot, aIRQ, 255);
538
539 AutoCaller autoCaller(this);
540 CheckComRCReturnRC(autoCaller.rc());
541
542 /* the machine needs to be mutable */
543 Machine::AutoMutableStateDependency adep (mParent);
544 CheckComRCReturnRC(adep.rc());
545
546 AutoWriteLock alock(this);
547
548 HRESULT rc = S_OK;
549 bool emitChangeEvent = false;
550
551 if (mData->mIRQ != aIRQ)
552 {
553 mData.backup();
554 mData->mIRQ = aIRQ;
555 emitChangeEvent = true;
556 }
557
558 if (emitChangeEvent)
559 {
560 /* leave the lock before informing callbacks */
561 alock.unlock();
562
563 mParent->onSerialPortChange (this);
564 }
565
566 return rc;
567}
568
569STDMETHODIMP SerialPort::COMGETTER(IOBase) (ULONG *aIOBase)
570{
571 CheckComArgOutPointerValid(aIOBase);
572
573 AutoCaller autoCaller(this);
574 CheckComRCReturnRC(autoCaller.rc());
575
576 AutoReadLock alock(this);
577
578 *aIOBase = mData->mIOBase;
579
580 return S_OK;
581}
582
583STDMETHODIMP SerialPort::COMSETTER(IOBase)(ULONG aIOBase)
584{
585 /* check IOBase limits
586 * (when changing this, make sure it corresponds to XML schema */
587 if (aIOBase > 0xFFFF)
588 return setError (E_INVALIDARG,
589 tr ("Invalid I/O port base address of the serial port %d: "
590 "%lu (must be in range [0, 0x%X])"),
591 mData->mSlot, aIOBase, 0, 0xFFFF);
592
593 AutoCaller autoCaller(this);
594 CheckComRCReturnRC(autoCaller.rc());
595
596 /* the machine needs to be mutable */
597 Machine::AutoMutableStateDependency adep (mParent);
598 CheckComRCReturnRC(adep.rc());
599
600 AutoWriteLock alock(this);
601
602 HRESULT rc = S_OK;
603 bool emitChangeEvent = false;
604
605 if (mData->mIOBase != aIOBase)
606 {
607 mData.backup();
608 mData->mIOBase = aIOBase;
609 emitChangeEvent = true;
610 }
611
612 if (emitChangeEvent)
613 {
614 /* leave the lock before informing callbacks */
615 alock.unlock();
616
617 mParent->onSerialPortChange (this);
618 }
619
620 return rc;
621}
622
623STDMETHODIMP SerialPort::COMGETTER(Path) (BSTR *aPath)
624{
625 CheckComArgOutPointerValid(aPath);
626
627 AutoCaller autoCaller(this);
628 CheckComRCReturnRC(autoCaller.rc());
629
630 AutoReadLock alock(this);
631
632 mData->mPath.cloneTo(aPath);
633
634 return S_OK;
635}
636
637/**
638 * Validates COMSETTER(Path) arguments.
639 */
640HRESULT SerialPort::checkSetPath (CBSTR aPath)
641{
642 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
643
644 if ((mData->mHostMode == PortMode_HostDevice ||
645 mData->mHostMode == PortMode_HostPipe ||
646 mData->mHostMode == PortMode_RawFile) &&
647 (aPath == NULL || *aPath == '\0'))
648 return setError (E_INVALIDARG,
649 tr ("Path of the serial port %d may not be empty or null in "
650 "host pipe or host device mode"),
651 mData->mSlot);
652
653 return S_OK;
654}
655
656STDMETHODIMP SerialPort::COMSETTER(Path) (IN_BSTR aPath)
657{
658 AutoCaller autoCaller(this);
659 CheckComRCReturnRC(autoCaller.rc());
660
661 /* the machine needs to be mutable */
662 Machine::AutoMutableStateDependency adep (mParent);
663 CheckComRCReturnRC(adep.rc());
664
665 AutoWriteLock alock(this);
666
667 /* we treat empty as null when e.g. saving to XML, do the same here */
668 if (aPath && *aPath == '\0')
669 aPath = NULL;
670
671 if (mData->mPath != aPath)
672 {
673 HRESULT rc = checkSetPath (aPath);
674 CheckComRCReturnRC(rc);
675
676 mData.backup();
677 mData->mPath = aPath;
678
679 /* leave the lock before informing callbacks */
680 alock.unlock();
681
682 return mParent->onSerialPortChange (this);
683 }
684
685 return S_OK;
686}
687
688STDMETHODIMP SerialPort::COMGETTER(Server) (BOOL *aServer)
689{
690 CheckComArgOutPointerValid(aServer);
691
692 AutoCaller autoCaller(this);
693 CheckComRCReturnRC(autoCaller.rc());
694
695 AutoReadLock alock(this);
696
697 *aServer = mData->mServer;
698
699 return S_OK;
700}
701
702STDMETHODIMP SerialPort::COMSETTER(Server) (BOOL aServer)
703{
704 AutoCaller autoCaller(this);
705 CheckComRCReturnRC(autoCaller.rc());
706
707 /* the machine needs to be mutable */
708 Machine::AutoMutableStateDependency adep (mParent);
709 CheckComRCReturnRC(adep.rc());
710
711 AutoWriteLock alock(this);
712
713 if (mData->mServer != aServer)
714 {
715 mData.backup();
716 mData->mServer = aServer;
717
718 /* leave the lock before informing callbacks */
719 alock.unlock();
720
721 mParent->onSerialPortChange (this);
722 }
723
724 return S_OK;
725}
726/* 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