VirtualBox

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

Last change on this file since 14902 was 14772, checked in by vboxsync, 16 years ago

Added vim modelines to aid following coding guidelines, like no tabs,
similar to what is already in the xidl file.

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