VirtualBox

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

Last change on this file since 62514 was 61549, checked in by vboxsync, 9 years ago

bugref:8238 VBoxSVC settings - BIOSSettings ParallelPort SerialPortImpl

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