VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/SerialPortImpl.cpp@ 73167

Last change on this file since 73167 was 72973, checked in by vboxsync, 6 years ago

Main: Some early sketches on how to get proper C++ enums with xpidl.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.0 KB
Line 
1/* $Id: SerialPortImpl.cpp 72973 2018-07-08 13:23:58Z vboxsync $ */
2/** @file
3 *
4 * VirtualBox COM class implementation
5 */
6
7/*
8 * Copyright (C) 2006-2017 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include "SerialPortImpl.h"
20#include "MachineImpl.h"
21#include "VirtualBoxImpl.h"
22#include "GuestOSTypeImpl.h"
23
24#include <iprt/assert.h>
25#include <iprt/string.h>
26#include <iprt/cpp/utils.h>
27
28#include <VBox/settings.h>
29
30#include "AutoStateDep.h"
31#include "AutoCaller.h"
32#include "Logging.h"
33
34//////////////////////////////////////////////////////////////////////////////////
35//
36// SerialPort private data definition
37//
38//////////////////////////////////////////////////////////////////////////////////
39
40struct SerialPort::Data
41{
42 Data()
43 : fModified(false),
44 pMachine(NULL)
45 { }
46
47 bool fModified;
48 Machine * const pMachine;
49 const ComObjPtr<SerialPort> pPeer;
50 Backupable<settings::SerialPort> bd;
51};
52
53// constructor / destructor
54/////////////////////////////////////////////////////////////////////////////
55
56DEFINE_EMPTY_CTOR_DTOR(SerialPort)
57
58HRESULT SerialPort::FinalConstruct()
59{
60 return BaseFinalConstruct();
61}
62
63void SerialPort::FinalRelease()
64{
65 uninit();
66 BaseFinalRelease();
67}
68
69// public initializer/uninitializer for internal purposes only
70/////////////////////////////////////////////////////////////////////////////
71
72/**
73 * Initializes the Serial Port object.
74 *
75 * @param aParent Handle of the parent object.
76 * @param aSlot Slot number the serial port is plugged into.
77 */
78HRESULT SerialPort::init(Machine *aParent, ULONG aSlot)
79{
80 LogFlowThisFunc(("aParent=%p, aSlot=%d\n", aParent, aSlot));
81
82 ComAssertRet(aParent, E_INVALIDARG);
83
84 /* Enclose the state transition NotReady->InInit->Ready */
85 AutoInitSpan autoInitSpan(this);
86 AssertReturn(autoInitSpan.isOk(), E_FAIL);
87
88 m = new Data();
89
90 unconst(m->pMachine) = aParent;
91 /* m->pPeer is left null */
92
93 m->bd.allocate();
94
95 /* initialize data */
96 m->bd->ulSlot = aSlot;
97
98 /* Confirm a successful initialization */
99 autoInitSpan.setSucceeded();
100
101 return S_OK;
102}
103
104/**
105 * Initializes the Serial Port object given another serial port object
106 * (a kind of copy constructor). This object shares data with
107 * the object passed as an argument.
108 *
109 * @note This object must be destroyed before the original object
110 * it shares data with is destroyed.
111 *
112 * @note Locks @a aThat object for reading.
113 */
114HRESULT SerialPort::init(Machine *aParent, SerialPort *aThat)
115{
116 LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat));
117
118 ComAssertRet(aParent && aThat, E_INVALIDARG);
119
120 /* Enclose the state transition NotReady->InInit->Ready */
121 AutoInitSpan autoInitSpan(this);
122 AssertReturn(autoInitSpan.isOk(), E_FAIL);
123
124 m = new Data();
125
126 unconst(m->pMachine) = aParent;
127 unconst(m->pPeer) = aThat;
128
129 AutoCaller thatCaller(aThat);
130 AssertComRCReturnRC(thatCaller.rc());
131
132 AutoReadLock thatLock(aThat COMMA_LOCKVAL_SRC_POS);
133 m->bd.share(aThat->m->bd);
134
135 /* Confirm a successful initialization */
136 autoInitSpan.setSucceeded();
137
138 return S_OK;
139}
140
141/**
142 * Initializes the guest object given another guest object
143 * (a kind of copy constructor). This object makes a private copy of data
144 * of the original object passed as an argument.
145 *
146 * @note Locks @a aThat object for reading.
147 */
148HRESULT SerialPort::initCopy(Machine *aParent, SerialPort *aThat)
149{
150 LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat));
151
152 ComAssertRet(aParent && aThat, E_INVALIDARG);
153
154 /* Enclose the state transition NotReady->InInit->Ready */
155 AutoInitSpan autoInitSpan(this);
156 AssertReturn(autoInitSpan.isOk(), E_FAIL);
157
158 m = new Data();
159
160 unconst(m->pMachine) = aParent;
161 /* pPeer is left null */
162
163 AutoCaller thatCaller(aThat);
164 AssertComRCReturnRC(thatCaller.rc());
165
166 AutoReadLock thatLock(aThat COMMA_LOCKVAL_SRC_POS);
167 m->bd.attachCopy(aThat->m->bd);
168
169 /* Confirm a successful initialization */
170 autoInitSpan.setSucceeded();
171
172 return S_OK;
173}
174
175/**
176 * Uninitializes the instance and sets the ready flag to FALSE.
177 * Called either from FinalRelease() or by the parent when it gets destroyed.
178 */
179void SerialPort::uninit()
180{
181 LogFlowThisFunc(("\n"));
182
183 /* Enclose the state transition Ready->InUninit->NotReady */
184 AutoUninitSpan autoUninitSpan(this);
185 if (autoUninitSpan.uninitDone())
186 return;
187
188 m->bd.free();
189
190 unconst(m->pPeer) = NULL;
191 unconst(m->pMachine) = NULL;
192
193 delete m;
194 m = NULL;
195}
196
197// ISerialPort properties
198/////////////////////////////////////////////////////////////////////////////
199
200HRESULT SerialPort::getEnabled(BOOL *aEnabled)
201{
202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
203
204 *aEnabled = m->bd->fEnabled;
205
206 return S_OK;
207}
208
209
210HRESULT SerialPort::setEnabled(BOOL aEnabled)
211{
212 LogFlowThisFunc(("aEnabled=%RTbool\n", aEnabled));
213
214 /* the machine needs to be mutable */
215 AutoMutableStateDependency adep(m->pMachine);
216 if (FAILED(adep.rc())) return adep.rc();
217
218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
219
220 if (m->bd->fEnabled != RT_BOOL(aEnabled))
221 {
222 m->bd.backup();
223 m->bd->fEnabled = RT_BOOL(aEnabled);
224
225 m->fModified = true;
226 // leave the lock before informing callbacks
227 alock.release();
228
229 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
230 m->pMachine->i_setModified(Machine::IsModified_SerialPorts);
231 mlock.release();
232
233 m->pMachine->i_onSerialPortChange(this);
234 }
235
236 return S_OK;
237}
238
239
240HRESULT SerialPort::getHostMode(PortMode_T *aHostMode)
241{
242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
243
244 *aHostMode = m->bd->portMode;
245
246 return S_OK;
247}
248
249HRESULT SerialPort::setHostMode(PortMode_T aHostMode)
250{
251 /* the machine needs to be mutable */
252 AutoMutableOrSavedOrRunningStateDependency adep(m->pMachine);
253 if (FAILED(adep.rc())) return adep.rc();
254
255 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
256
257 if (m->bd->portMode != aHostMode)
258 {
259 switch (aHostMode)
260 {
261 case PortMode_RawFile:
262 if (m->bd->strPath.isEmpty())
263 return setError(E_INVALIDARG,
264 tr("Cannot set the raw file mode of the serial port %d "
265 "because the file path is empty or null"),
266 m->bd->ulSlot);
267 break;
268 case PortMode_HostPipe:
269 if (m->bd->strPath.isEmpty())
270 return setError(E_INVALIDARG,
271 tr("Cannot set the host pipe mode of the serial port %d "
272 "because the pipe path is empty or null"),
273 m->bd->ulSlot);
274 break;
275 case PortMode_HostDevice:
276 if (m->bd->strPath.isEmpty())
277 return setError(E_INVALIDARG,
278 tr("Cannot set the host device mode of the serial port %d "
279 "because the device path is empty or null"),
280 m->bd->ulSlot);
281 break;
282 case PortMode_TCP:
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 server address or TCP port is invalid"),
287 m->bd->ulSlot);
288 break;
289 case PortMode_Disconnected:
290 break;
291#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
292 case PortMode_32BitHack: /* (compiler warnings) */
293 AssertFailedBreak();
294#endif
295 }
296
297 m->bd.backup();
298 m->bd->portMode = aHostMode;
299
300 m->fModified = true;
301 // leave the lock before informing callbacks
302 alock.release();
303
304 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
305 m->pMachine->i_setModified(Machine::IsModified_SerialPorts);
306 mlock.release();
307
308 m->pMachine->i_onSerialPortChange(this);
309 }
310
311 return S_OK;
312}
313
314HRESULT SerialPort::getSlot(ULONG *aSlot)
315{
316 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
317
318 *aSlot = m->bd->ulSlot;
319
320 return S_OK;
321}
322
323
324HRESULT SerialPort::getIRQ(ULONG *aIRQ)
325{
326 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
327
328 *aIRQ = m->bd->ulIRQ;
329
330 return S_OK;
331}
332
333
334HRESULT SerialPort::setIRQ(ULONG aIRQ)
335{
336 /* check IRQ limits
337 * (when changing this, make sure it corresponds to XML schema */
338 if (aIRQ > 255)
339 return setError(E_INVALIDARG,
340 tr("Invalid IRQ number of the serial port %d: %lu (must be in range [0, %lu])"),
341 m->bd->ulSlot, aIRQ, 255);
342
343 /* the machine needs to be mutable */
344 AutoMutableStateDependency adep(m->pMachine);
345 if (FAILED(adep.rc())) return adep.rc();
346
347 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
348
349 if (m->bd->ulIRQ != aIRQ)
350 {
351 m->bd.backup();
352 m->bd->ulIRQ = aIRQ;
353
354 m->fModified = true;
355 // leave the lock before informing callbacks
356 alock.release();
357
358 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
359 m->pMachine->i_setModified(Machine::IsModified_SerialPorts);
360 mlock.release();
361
362 m->pMachine->i_onSerialPortChange(this);
363 }
364
365 return S_OK;
366}
367
368
369HRESULT SerialPort::getIOBase(ULONG *aIOBase)
370{
371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
372
373 *aIOBase = m->bd->ulIOBase;
374
375 return S_OK;
376}
377
378HRESULT SerialPort::setIOBase(ULONG aIOBase)
379{
380 /* check IOBase limits
381 * (when changing this, make sure it corresponds to XML schema */
382 if (aIOBase > 0xFFFF)
383 return setError(E_INVALIDARG,
384 tr("Invalid I/O port base address of the serial port %d: %lu (must be in range [0, 0x%X])"),
385 m->bd->ulSlot, aIOBase, 0, 0xFFFF);
386
387 AutoCaller autoCaller(this);
388 if (FAILED(autoCaller.rc())) return autoCaller.rc();
389
390 /* the machine needs to be mutable */
391 AutoMutableStateDependency adep(m->pMachine);
392 if (FAILED(adep.rc())) return adep.rc();
393
394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
395
396 HRESULT rc = S_OK;
397
398 if (m->bd->ulIOBase != aIOBase)
399 {
400 m->bd.backup();
401 m->bd->ulIOBase = aIOBase;
402
403 m->fModified = true;
404 // leave the lock before informing callbacks
405 alock.release();
406
407 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
408 m->pMachine->i_setModified(Machine::IsModified_SerialPorts);
409 mlock.release();
410
411 m->pMachine->i_onSerialPortChange(this);
412 }
413
414 return rc;
415}
416
417HRESULT SerialPort::getPath(com::Utf8Str &aPath)
418{
419 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
420
421 aPath = m->bd->strPath;
422
423 return S_OK;
424}
425
426
427HRESULT SerialPort::setPath(const com::Utf8Str &aPath)
428{
429 /* the machine needs to be mutable */
430 AutoMutableOrSavedOrRunningStateDependency adep(m->pMachine);
431 if (FAILED(adep.rc())) return adep.rc();
432
433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
434
435 if (aPath != m->bd->strPath)
436 {
437 HRESULT rc = i_checkSetPath(aPath);
438 if (FAILED(rc)) return rc;
439
440 m->bd.backup();
441 m->bd->strPath = aPath;
442
443 m->fModified = true;
444 // leave the lock before informing callbacks
445 alock.release();
446
447 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
448 m->pMachine->i_setModified(Machine::IsModified_SerialPorts);
449 mlock.release();
450
451 m->pMachine->i_onSerialPortChange(this);
452 }
453
454 return S_OK;
455}
456
457HRESULT SerialPort::getServer(BOOL *aServer)
458{
459 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
460
461 *aServer = m->bd->fServer;
462
463 return S_OK;
464}
465
466HRESULT SerialPort::setServer(BOOL aServer)
467{
468 /* the machine needs to be mutable */
469 AutoMutableOrSavedOrRunningStateDependency adep(m->pMachine);
470 if (FAILED(adep.rc())) return adep.rc();
471
472 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
473
474 if (m->bd->fServer != RT_BOOL(aServer))
475 {
476 m->bd.backup();
477 m->bd->fServer = RT_BOOL(aServer);
478
479 m->fModified = true;
480 // leave the lock before informing callbacks
481 alock.release();
482
483 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
484 m->pMachine->i_setModified(Machine::IsModified_SerialPorts);
485 mlock.release();
486
487 m->pMachine->i_onSerialPortChange(this);
488 }
489
490 return S_OK;
491}
492
493// public methods only for internal purposes
494////////////////////////////////////////////////////////////////////////////////
495
496/**
497 * Loads settings from the given port node.
498 * May be called once right after this object creation.
499 *
500 * @param data Configuration settings.
501 *
502 * @note Locks this object for writing.
503 */
504HRESULT SerialPort::i_loadSettings(const settings::SerialPort &data)
505{
506
507 AutoCaller autoCaller(this);
508 AssertComRCReturnRC(autoCaller.rc());
509
510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
511
512 // simply copy
513 *m->bd.data() = data;
514
515 return S_OK;
516}
517
518/**
519 * Saves the port settings to the given port node.
520 *
521 * Note that the given Port node is completely empty on input.
522 *
523 * @param data Configuration settings.
524 *
525 * @note Locks this object for reading.
526 */
527HRESULT SerialPort::i_saveSettings(settings::SerialPort &data)
528{
529 AutoCaller autoCaller(this);
530 AssertComRCReturnRC(autoCaller.rc());
531
532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
533
534 // simply copy
535 data = *m->bd.data();
536
537 return S_OK;
538}
539
540/**
541 * Returns true if any setter method has modified settings of this instance.
542 * @return
543 */
544bool SerialPort::i_isModified()
545{
546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
547 return m->fModified;
548}
549
550/**
551 * @note Locks this object for writing.
552 */
553void SerialPort::i_rollback()
554{
555 /* sanity */
556 AutoCaller autoCaller(this);
557 AssertComRCReturnVoid(autoCaller.rc());
558
559 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
560
561 m->bd.rollback();
562}
563
564/**
565 * @note Locks this object for writing, together with the peer object (also
566 * for writing) if there is one.
567 */
568void SerialPort::i_commit()
569{
570 /* sanity */
571 AutoCaller autoCaller(this);
572 AssertComRCReturnVoid(autoCaller.rc());
573
574 /* sanity too */
575 AutoCaller peerCaller(m->pPeer);
576 AssertComRCReturnVoid(peerCaller.rc());
577
578 /* lock both for writing since we modify both (pPeer is "master" so locked
579 * first) */
580 AutoMultiWriteLock2 alock(m->pPeer, this COMMA_LOCKVAL_SRC_POS);
581
582 if (m->bd.isBackedUp())
583 {
584 m->bd.commit();
585 if (m->pPeer)
586 {
587 /* attach new data to the peer and reshare it */
588 m->pPeer->m->bd.attach(m->bd);
589 }
590 }
591}
592
593/**
594 * @note Locks this object for writing, together with the peer object
595 * represented by @a aThat (locked for reading).
596 */
597void SerialPort::i_copyFrom(SerialPort *aThat)
598{
599 AssertReturnVoid(aThat != NULL);
600
601 /* sanity */
602 AutoCaller autoCaller(this);
603 AssertComRCReturnVoid(autoCaller.rc());
604
605 /* sanity too */
606 AutoCaller thatCaller(aThat);
607 AssertComRCReturnVoid(thatCaller.rc());
608
609 /* peer is not modified, lock it for reading (aThat is "master" so locked
610 * first) */
611 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
612 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
613
614 /* this will back up current data */
615 m->bd.assignCopy(aThat->m->bd);
616}
617
618/**
619 * Applies the defaults for this serial port.
620 *
621 * @note This method currently assumes that the object is in the state after
622 * calling init(), it does not set defaults from an arbitrary state.
623 */
624void SerialPort::i_applyDefaults(GuestOSType *aOsType)
625{
626 /* sanity */
627 AutoCaller autoCaller(this);
628 AssertComRCReturnVoid(autoCaller.rc());
629
630 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
631
632 /* Set some more defaults. */
633 switch (m->bd->ulSlot)
634 {
635 case 0:
636 {
637 m->bd->ulIOBase = 0x3f8;
638 m->bd->ulIRQ = 4;
639 break;
640 }
641 case 1:
642 {
643 m->bd->ulIOBase = 0x2f8;
644 m->bd->ulIRQ = 3;
645 break;
646 }
647 case 2:
648 {
649 m->bd->ulIOBase = 0x3e8;
650 m->bd->ulIRQ = 4;
651 break;
652 }
653 case 3:
654 {
655 m->bd->ulIOBase = 0x2e8;
656 m->bd->ulIRQ = 3;
657 break;
658 }
659 default:
660 AssertMsgFailed(("Serial port slot %u exceeds limit\n", m->bd->ulSlot));
661 break;
662 }
663
664 uint32_t numSerialEnabled = 0;
665 if (aOsType)
666 numSerialEnabled = aOsType->i_numSerialEnabled();
667
668 /* Enable port if requested */
669 if (m->bd->ulSlot < numSerialEnabled)
670 {
671 m->bd->fEnabled = true;
672 }
673}
674
675bool SerialPort::i_hasDefaults()
676{
677 /* sanity */
678 AutoCaller autoCaller(this);
679 AssertComRCReturn(autoCaller.rc(), true);
680
681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
682
683 if ( !m->bd->fEnabled
684 && m->bd->portMode == PortMode_Disconnected
685 && !m->bd->fServer)
686 {
687 /* Could be default, check the IO base and IRQ. */
688 switch (m->bd->ulSlot)
689 {
690 case 0:
691 if (m->bd->ulIOBase == 0x3f8 && m->bd->ulIRQ == 4)
692 return true;
693 break;
694 case 1:
695 if (m->bd->ulIOBase == 0x2f8 && m->bd->ulIRQ == 3)
696 return true;
697 break;
698 case 2:
699 if (m->bd->ulIOBase == 0x3e8 && m->bd->ulIRQ == 4)
700 return true;
701 break;
702 case 3:
703 if (m->bd->ulIOBase == 0x2e8 && m->bd->ulIRQ == 3)
704 return true;
705 break;
706 default:
707 AssertMsgFailed(("Serial port slot %u exceeds limit\n", m->bd->ulSlot));
708 break;
709 }
710
711 /* Detect old-style defaults (0x3f8, irq 4) in any slot, they are still
712 * in place for many VMs created by old VirtualBox versions. */
713 if (m->bd->ulIOBase == 0x3f8 && m->bd->ulIRQ == 4)
714 return true;
715 }
716
717 return false;
718}
719
720/**
721 * Validates COMSETTER(Path) arguments.
722 */
723HRESULT SerialPort::i_checkSetPath(const Utf8Str &str)
724{
725 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
726
727 if ( ( m->bd->portMode == PortMode_HostDevice
728 || m->bd->portMode == PortMode_HostPipe
729 || m->bd->portMode == PortMode_TCP
730 || m->bd->portMode == PortMode_RawFile
731 ) && str.isEmpty()
732 )
733 return setError(E_INVALIDARG,
734 tr("Path of the serial port %d may not be empty or null in "
735 "host pipe, host device or TCP mode"),
736 m->bd->ulSlot);
737
738 return S_OK;
739}
740
741/* 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