VirtualBox

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

Last change on this file since 55336 was 55259, checked in by vboxsync, 10 years ago

Main/Serial+Devices/Serial: new TCP backend for serial port. Contributed by Alexey Eromenko. Thanks!
Frontends/VirtualBox+VBoxManage: adapted accordingly

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