VirtualBox

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

Last change on this file since 70772 was 70766, checked in by vboxsync, 6 years ago

Main,FE/VBoxManage: Allow changing the serial port attachment type during runtime

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.8 KB
Line 
1/* $Id: SerialPortImpl.cpp 70766 2018-01-28 20:53:14Z 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 }
292
293 m->bd.backup();
294 m->bd->portMode = aHostMode;
295
296 m->fModified = true;
297 // leave the lock before informing callbacks
298 alock.release();
299
300 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
301 m->pMachine->i_setModified(Machine::IsModified_SerialPorts);
302 mlock.release();
303
304 m->pMachine->i_onSerialPortChange(this);
305 }
306
307 return S_OK;
308}
309
310HRESULT SerialPort::getSlot(ULONG *aSlot)
311{
312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
313
314 *aSlot = m->bd->ulSlot;
315
316 return S_OK;
317}
318
319
320HRESULT SerialPort::getIRQ(ULONG *aIRQ)
321{
322 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
323
324 *aIRQ = m->bd->ulIRQ;
325
326 return S_OK;
327}
328
329
330HRESULT SerialPort::setIRQ(ULONG aIRQ)
331{
332 /* check IRQ limits
333 * (when changing this, make sure it corresponds to XML schema */
334 if (aIRQ > 255)
335 return setError(E_INVALIDARG,
336 tr("Invalid IRQ number of the serial port %d: %lu (must be in range [0, %lu])"),
337 m->bd->ulSlot, aIRQ, 255);
338
339 /* the machine needs to be mutable */
340 AutoMutableStateDependency adep(m->pMachine);
341 if (FAILED(adep.rc())) return adep.rc();
342
343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
344
345 if (m->bd->ulIRQ != aIRQ)
346 {
347 m->bd.backup();
348 m->bd->ulIRQ = aIRQ;
349
350 m->fModified = true;
351 // leave the lock before informing callbacks
352 alock.release();
353
354 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
355 m->pMachine->i_setModified(Machine::IsModified_SerialPorts);
356 mlock.release();
357
358 m->pMachine->i_onSerialPortChange(this);
359 }
360
361 return S_OK;
362}
363
364
365HRESULT SerialPort::getIOBase(ULONG *aIOBase)
366{
367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
368
369 *aIOBase = m->bd->ulIOBase;
370
371 return S_OK;
372}
373
374HRESULT SerialPort::setIOBase(ULONG aIOBase)
375{
376 /* check IOBase limits
377 * (when changing this, make sure it corresponds to XML schema */
378 if (aIOBase > 0xFFFF)
379 return setError(E_INVALIDARG,
380 tr("Invalid I/O port base address of the serial port %d: %lu (must be in range [0, 0x%X])"),
381 m->bd->ulSlot, aIOBase, 0, 0xFFFF);
382
383 AutoCaller autoCaller(this);
384 if (FAILED(autoCaller.rc())) return autoCaller.rc();
385
386 /* the machine needs to be mutable */
387 AutoMutableStateDependency adep(m->pMachine);
388 if (FAILED(adep.rc())) return adep.rc();
389
390 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
391
392 HRESULT rc = S_OK;
393
394 if (m->bd->ulIOBase != aIOBase)
395 {
396 m->bd.backup();
397 m->bd->ulIOBase = aIOBase;
398
399 m->fModified = true;
400 // leave the lock before informing callbacks
401 alock.release();
402
403 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
404 m->pMachine->i_setModified(Machine::IsModified_SerialPorts);
405 mlock.release();
406
407 m->pMachine->i_onSerialPortChange(this);
408 }
409
410 return rc;
411}
412
413HRESULT SerialPort::getPath(com::Utf8Str &aPath)
414{
415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
416
417 aPath = m->bd->strPath;
418
419 return S_OK;
420}
421
422
423HRESULT SerialPort::setPath(const com::Utf8Str &aPath)
424{
425 /* the machine needs to be mutable */
426 AutoMutableOrSavedOrRunningStateDependency adep(m->pMachine);
427 if (FAILED(adep.rc())) return adep.rc();
428
429 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
430
431 if (aPath != m->bd->strPath)
432 {
433 HRESULT rc = i_checkSetPath(aPath);
434 if (FAILED(rc)) return rc;
435
436 m->bd.backup();
437 m->bd->strPath = aPath;
438
439 m->fModified = true;
440 // leave the lock before informing callbacks
441 alock.release();
442
443 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
444 m->pMachine->i_setModified(Machine::IsModified_SerialPorts);
445 mlock.release();
446
447 m->pMachine->i_onSerialPortChange(this);
448 }
449
450 return S_OK;
451}
452
453HRESULT SerialPort::getServer(BOOL *aServer)
454{
455 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
456
457 *aServer = m->bd->fServer;
458
459 return S_OK;
460}
461
462HRESULT SerialPort::setServer(BOOL aServer)
463{
464 /* the machine needs to be mutable */
465 AutoMutableOrSavedOrRunningStateDependency adep(m->pMachine);
466 if (FAILED(adep.rc())) return adep.rc();
467
468 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
469
470 if (m->bd->fServer != RT_BOOL(aServer))
471 {
472 m->bd.backup();
473 m->bd->fServer = RT_BOOL(aServer);
474
475 m->fModified = true;
476 // leave the lock before informing callbacks
477 alock.release();
478
479 AutoWriteLock mlock(m->pMachine COMMA_LOCKVAL_SRC_POS);
480 m->pMachine->i_setModified(Machine::IsModified_SerialPorts);
481 mlock.release();
482
483 m->pMachine->i_onSerialPortChange(this);
484 }
485
486 return S_OK;
487}
488
489// public methods only for internal purposes
490////////////////////////////////////////////////////////////////////////////////
491
492/**
493 * Loads settings from the given port node.
494 * May be called once right after this object creation.
495 *
496 * @param data Configuration settings.
497 *
498 * @note Locks this object for writing.
499 */
500HRESULT SerialPort::i_loadSettings(const settings::SerialPort &data)
501{
502
503 AutoCaller autoCaller(this);
504 AssertComRCReturnRC(autoCaller.rc());
505
506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
507
508 // simply copy
509 *m->bd.data() = data;
510
511 return S_OK;
512}
513
514/**
515 * Saves the port settings to the given port node.
516 *
517 * Note that the given Port node is completely empty on input.
518 *
519 * @param data Configuration settings.
520 *
521 * @note Locks this object for reading.
522 */
523HRESULT SerialPort::i_saveSettings(settings::SerialPort &data)
524{
525 AutoCaller autoCaller(this);
526 AssertComRCReturnRC(autoCaller.rc());
527
528 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
529
530 // simply copy
531 data = *m->bd.data();
532
533 return S_OK;
534}
535
536/**
537 * Returns true if any setter method has modified settings of this instance.
538 * @return
539 */
540bool SerialPort::i_isModified()
541{
542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
543 return m->fModified;
544}
545
546/**
547 * @note Locks this object for writing.
548 */
549void SerialPort::i_rollback()
550{
551 /* sanity */
552 AutoCaller autoCaller(this);
553 AssertComRCReturnVoid(autoCaller.rc());
554
555 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
556
557 m->bd.rollback();
558}
559
560/**
561 * @note Locks this object for writing, together with the peer object (also
562 * for writing) if there is one.
563 */
564void SerialPort::i_commit()
565{
566 /* sanity */
567 AutoCaller autoCaller(this);
568 AssertComRCReturnVoid(autoCaller.rc());
569
570 /* sanity too */
571 AutoCaller peerCaller(m->pPeer);
572 AssertComRCReturnVoid(peerCaller.rc());
573
574 /* lock both for writing since we modify both (pPeer is "master" so locked
575 * first) */
576 AutoMultiWriteLock2 alock(m->pPeer, this COMMA_LOCKVAL_SRC_POS);
577
578 if (m->bd.isBackedUp())
579 {
580 m->bd.commit();
581 if (m->pPeer)
582 {
583 /* attach new data to the peer and reshare it */
584 m->pPeer->m->bd.attach(m->bd);
585 }
586 }
587}
588
589/**
590 * @note Locks this object for writing, together with the peer object
591 * represented by @a aThat (locked for reading).
592 */
593void SerialPort::i_copyFrom(SerialPort *aThat)
594{
595 AssertReturnVoid(aThat != NULL);
596
597 /* sanity */
598 AutoCaller autoCaller(this);
599 AssertComRCReturnVoid(autoCaller.rc());
600
601 /* sanity too */
602 AutoCaller thatCaller(aThat);
603 AssertComRCReturnVoid(thatCaller.rc());
604
605 /* peer is not modified, lock it for reading (aThat is "master" so locked
606 * first) */
607 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
608 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
609
610 /* this will back up current data */
611 m->bd.assignCopy(aThat->m->bd);
612}
613
614/**
615 * Applies the defaults for this serial port.
616 *
617 * @note This method currently assumes that the object is in the state after
618 * calling init(), it does not set defaults from an arbitrary state.
619 */
620void SerialPort::i_applyDefaults(GuestOSType *aOsType)
621{
622 /* sanity */
623 AutoCaller autoCaller(this);
624 AssertComRCReturnVoid(autoCaller.rc());
625
626 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
627
628 /* Set some more defaults. */
629 switch (m->bd->ulSlot)
630 {
631 case 0:
632 {
633 m->bd->ulIOBase = 0x3f8;
634 m->bd->ulIRQ = 4;
635 break;
636 }
637 case 1:
638 {
639 m->bd->ulIOBase = 0x2f8;
640 m->bd->ulIRQ = 3;
641 break;
642 }
643 case 2:
644 {
645 m->bd->ulIOBase = 0x3e8;
646 m->bd->ulIRQ = 4;
647 break;
648 }
649 case 3:
650 {
651 m->bd->ulIOBase = 0x2e8;
652 m->bd->ulIRQ = 3;
653 break;
654 }
655 default:
656 AssertMsgFailed(("Serial port slot %u exceeds limit\n", m->bd->ulSlot));
657 break;
658 }
659
660 uint32_t numSerialEnabled = 0;
661 if (aOsType)
662 numSerialEnabled = aOsType->i_numSerialEnabled();
663
664 /* Enable port if requested */
665 if (m->bd->ulSlot < numSerialEnabled)
666 {
667 m->bd->fEnabled = true;
668 }
669}
670
671bool SerialPort::i_hasDefaults()
672{
673 /* sanity */
674 AutoCaller autoCaller(this);
675 AssertComRCReturn(autoCaller.rc(), true);
676
677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
678
679 if ( !m->bd->fEnabled
680 && m->bd->portMode == PortMode_Disconnected
681 && !m->bd->fServer)
682 {
683 /* Could be default, check the IO base and IRQ. */
684 switch (m->bd->ulSlot)
685 {
686 case 0:
687 if (m->bd->ulIOBase == 0x3f8 && m->bd->ulIRQ == 4)
688 return true;
689 break;
690 case 1:
691 if (m->bd->ulIOBase == 0x2f8 && m->bd->ulIRQ == 3)
692 return true;
693 break;
694 case 2:
695 if (m->bd->ulIOBase == 0x3e8 && m->bd->ulIRQ == 4)
696 return true;
697 break;
698 case 3:
699 if (m->bd->ulIOBase == 0x2e8 && m->bd->ulIRQ == 3)
700 return true;
701 break;
702 default:
703 AssertMsgFailed(("Serial port slot %u exceeds limit\n", m->bd->ulSlot));
704 break;
705 }
706
707 /* Detect old-style defaults (0x3f8, irq 4) in any slot, they are still
708 * in place for many VMs created by old VirtualBox versions. */
709 if (m->bd->ulIOBase == 0x3f8 && m->bd->ulIRQ == 4)
710 return true;
711 }
712
713 return false;
714}
715
716/**
717 * Validates COMSETTER(Path) arguments.
718 */
719HRESULT SerialPort::i_checkSetPath(const Utf8Str &str)
720{
721 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
722
723 if ( ( m->bd->portMode == PortMode_HostDevice
724 || m->bd->portMode == PortMode_HostPipe
725 || m->bd->portMode == PortMode_TCP
726 || m->bd->portMode == PortMode_RawFile
727 ) && str.isEmpty()
728 )
729 return setError(E_INVALIDARG,
730 tr("Path of the serial port %d may not be empty or null in "
731 "host pipe, host device or TCP mode"),
732 m->bd->ulSlot);
733
734 return S_OK;
735}
736
737/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use