VirtualBox

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

Last change on this file since 25881 was 25860, checked in by vboxsync, 15 years ago

Main: cleanup: get rid of VirtualBoxBaseProto, move AutoCaller*/*Span* classes out of VirtualBoxBaseProto class scope and into separate header; move CombinedProgress into separate header (it's only used by Console any more)

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