VirtualBox

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

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

Main: back out r42503

  • 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'", 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",
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 CheckComArgOutPointerValid(aEnabled);
362
363 AutoCaller autoCaller (this);
364 CheckComRCReturnRC (autoCaller.rc());
365
366 AutoReadLock alock (this);
367
368 *aEnabled = mData->mEnabled;
369
370 return S_OK;
371}
372
373STDMETHODIMP SerialPort::COMSETTER(Enabled) (BOOL aEnabled)
374{
375 LogFlowThisFunc (("aEnabled=%RTbool\n", aEnabled));
376
377 AutoCaller autoCaller (this);
378 CheckComRCReturnRC (autoCaller.rc());
379
380 /* the machine needs to be mutable */
381 Machine::AutoMutableStateDependency adep (mParent);
382 CheckComRCReturnRC (adep.rc());
383
384 AutoWriteLock alock (this);
385
386 if (mData->mEnabled != aEnabled)
387 {
388 mData.backup();
389 mData->mEnabled = aEnabled;
390
391 /* leave the lock before informing callbacks */
392 alock.unlock();
393
394 mParent->onSerialPortChange (this);
395 }
396
397 return S_OK;
398}
399
400STDMETHODIMP SerialPort::COMGETTER(HostMode) (PortMode_T *aHostMode)
401{
402 CheckComArgOutPointerValid(aHostMode);
403
404 AutoCaller autoCaller (this);
405 CheckComRCReturnRC (autoCaller.rc());
406
407 AutoReadLock alock (this);
408
409 *aHostMode = mData->mHostMode;
410
411 return S_OK;
412}
413
414STDMETHODIMP SerialPort::COMSETTER(HostMode) (PortMode_T aHostMode)
415{
416 AutoCaller autoCaller (this);
417 CheckComRCReturnRC (autoCaller.rc());
418
419 /* the machine needs to be mutable */
420 Machine::AutoMutableStateDependency adep (mParent);
421 CheckComRCReturnRC (adep.rc());
422
423 AutoWriteLock alock (this);
424
425 HRESULT rc = S_OK;
426 bool emitChangeEvent = false;
427
428 if (mData->mHostMode != aHostMode)
429 {
430 switch (aHostMode)
431 {
432 case PortMode_HostPipe:
433 if (mData->mPath.isEmpty())
434 return setError (E_INVALIDARG,
435 tr ("Cannot set the host pipe mode of the serial port %d "
436 "because the pipe path is empty or null"),
437 mData->mSlot);
438 break;
439 case PortMode_HostDevice:
440 if (mData->mPath.isEmpty())
441 return setError (E_INVALIDARG,
442 tr ("Cannot set the host device mode of the serial port %d "
443 "because the device path is empty or null"),
444 mData->mSlot);
445 break;
446 case PortMode_Disconnected:
447 break;
448 }
449
450 mData.backup();
451 mData->mHostMode = aHostMode;
452
453 emitChangeEvent = true;
454 }
455
456 if (emitChangeEvent)
457 {
458 /* leave the lock before informing callbacks */
459 alock.unlock();
460
461 mParent->onSerialPortChange (this);
462 }
463
464 return rc;
465}
466
467STDMETHODIMP SerialPort::COMGETTER(Slot) (ULONG *aSlot)
468{
469 CheckComArgOutPointerValid(aSlot);
470
471 AutoCaller autoCaller (this);
472 CheckComRCReturnRC (autoCaller.rc());
473
474 AutoReadLock alock (this);
475
476 *aSlot = mData->mSlot;
477
478 return S_OK;
479}
480
481STDMETHODIMP SerialPort::COMGETTER(IRQ) (ULONG *aIRQ)
482{
483 CheckComArgOutPointerValid(aIRQ);
484
485 AutoCaller autoCaller (this);
486 CheckComRCReturnRC (autoCaller.rc());
487
488 AutoReadLock alock (this);
489
490 *aIRQ = mData->mIRQ;
491
492 return S_OK;
493}
494
495STDMETHODIMP SerialPort::COMSETTER(IRQ)(ULONG aIRQ)
496{
497 /* check IRQ limits
498 * (when changing this, make sure it corresponds to XML schema */
499 if (aIRQ > 255)
500 return setError (E_INVALIDARG,
501 tr ("Invalid IRQ number of the serial port %d: "
502 "%lu (must be in range [0, %lu])"),
503 mData->mSlot, aIRQ, 255);
504
505 AutoCaller autoCaller (this);
506 CheckComRCReturnRC (autoCaller.rc());
507
508 /* the machine needs to be mutable */
509 Machine::AutoMutableStateDependency adep (mParent);
510 CheckComRCReturnRC (adep.rc());
511
512 AutoWriteLock alock (this);
513
514 HRESULT rc = S_OK;
515 bool emitChangeEvent = false;
516
517 if (mData->mIRQ != aIRQ)
518 {
519 mData.backup();
520 mData->mIRQ = aIRQ;
521 emitChangeEvent = true;
522 }
523
524 if (emitChangeEvent)
525 {
526 /* leave the lock before informing callbacks */
527 alock.unlock();
528
529 mParent->onSerialPortChange (this);
530 }
531
532 return rc;
533}
534
535STDMETHODIMP SerialPort::COMGETTER(IOBase) (ULONG *aIOBase)
536{
537 CheckComArgOutPointerValid(aIOBase);
538
539 AutoCaller autoCaller (this);
540 CheckComRCReturnRC (autoCaller.rc());
541
542 AutoReadLock alock (this);
543
544 *aIOBase = mData->mIOBase;
545
546 return S_OK;
547}
548
549STDMETHODIMP SerialPort::COMSETTER(IOBase)(ULONG aIOBase)
550{
551 /* check IOBase limits
552 * (when changing this, make sure it corresponds to XML schema */
553 if (aIOBase > 0xFFFF)
554 return setError (E_INVALIDARG,
555 tr ("Invalid I/O port base address of the serial port %d: "
556 "%lu (must be in range [0, 0x%X])"),
557 mData->mSlot, aIOBase, 0, 0xFFFF);
558
559 AutoCaller autoCaller (this);
560 CheckComRCReturnRC (autoCaller.rc());
561
562 /* the machine needs to be mutable */
563 Machine::AutoMutableStateDependency adep (mParent);
564 CheckComRCReturnRC (adep.rc());
565
566 AutoWriteLock alock (this);
567
568 HRESULT rc = S_OK;
569 bool emitChangeEvent = false;
570
571 if (mData->mIOBase != aIOBase)
572 {
573 mData.backup();
574 mData->mIOBase = aIOBase;
575 emitChangeEvent = true;
576 }
577
578 if (emitChangeEvent)
579 {
580 /* leave the lock before informing callbacks */
581 alock.unlock();
582
583 mParent->onSerialPortChange (this);
584 }
585
586 return rc;
587}
588
589STDMETHODIMP SerialPort::COMGETTER(Path) (BSTR *aPath)
590{
591 CheckComArgOutPointerValid(aPath);
592
593 AutoCaller autoCaller (this);
594 CheckComRCReturnRC (autoCaller.rc());
595
596 AutoReadLock alock (this);
597
598 mData->mPath.cloneTo (aPath);
599
600 return S_OK;
601}
602
603/**
604 * Validates COMSETTER(Path) arguments.
605 */
606HRESULT SerialPort::checkSetPath (CBSTR aPath)
607{
608 AssertReturn (isWriteLockOnCurrentThread(), E_FAIL);
609
610 if ((mData->mHostMode == PortMode_HostDevice ||
611 mData->mHostMode == PortMode_HostPipe) &&
612 (aPath == NULL || *aPath == '\0'))
613 return setError (E_INVALIDARG,
614 tr ("Path of the serial port %d may not be empty or null in "
615 "host pipe or host device mode"),
616 mData->mSlot);
617
618 return S_OK;
619}
620
621STDMETHODIMP SerialPort::COMSETTER(Path) (IN_BSTR aPath)
622{
623 AutoCaller autoCaller (this);
624 CheckComRCReturnRC (autoCaller.rc());
625
626 /* the machine needs to be mutable */
627 Machine::AutoMutableStateDependency adep (mParent);
628 CheckComRCReturnRC (adep.rc());
629
630 AutoWriteLock alock (this);
631
632 /* we treat empty as null when e.g. saving to XML, do the same here */
633 if (aPath && *aPath == '\0')
634 aPath = NULL;
635
636 if (mData->mPath != aPath)
637 {
638 HRESULT rc = checkSetPath (aPath);
639 CheckComRCReturnRC (rc);
640
641 mData.backup();
642 mData->mPath = aPath;
643
644 /* leave the lock before informing callbacks */
645 alock.unlock();
646
647 return mParent->onSerialPortChange (this);
648 }
649
650 return S_OK;
651}
652
653STDMETHODIMP SerialPort::COMGETTER(Server) (BOOL *aServer)
654{
655 CheckComArgOutPointerValid(aServer);
656
657 AutoCaller autoCaller (this);
658 CheckComRCReturnRC (autoCaller.rc());
659
660 AutoReadLock alock (this);
661
662 *aServer = mData->mServer;
663
664 return S_OK;
665}
666
667STDMETHODIMP SerialPort::COMSETTER(Server) (BOOL aServer)
668{
669 AutoCaller autoCaller (this);
670 CheckComRCReturnRC (autoCaller.rc());
671
672 /* the machine needs to be mutable */
673 Machine::AutoMutableStateDependency adep (mParent);
674 CheckComRCReturnRC (adep.rc());
675
676 AutoWriteLock alock (this);
677
678 if (mData->mServer != aServer)
679 {
680 mData.backup();
681 mData->mServer = aServer;
682
683 /* leave the lock before informing callbacks */
684 alock.unlock();
685
686 mParent->onSerialPortChange (this);
687 }
688
689 return S_OK;
690}
691/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use