VirtualBox

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

Last change on this file since 8123 was 8083, checked in by vboxsync, 17 years ago

Main: Renamed AutoLock => AutoWriteLock; AutoReaderLock => AutoReadLock.

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