VirtualBox

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

Last change on this file since 25414 was 25346, checked in by vboxsync, 14 years ago

iprt/cpputils.h -> iprt/cpp/utils.h

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

© 2023 Oracle
ContactPrivacy policyTerms of Use