VirtualBox

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

Last change on this file since 33000 was 32659, checked in by vboxsync, 14 years ago

Main: clarification

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

© 2023 Oracle
ContactPrivacy policyTerms of Use