VirtualBox

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

Last change on this file since 5806 was 5806, checked in by vboxsync, 17 years ago

Main: Fixed: Always save values of unused attributes of serial ports to preserve user's settings for later use.

File size: 16.8 KB
Line 
1/** @file
2 *
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * 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 "Logging.h"
22
23#include <iprt/string.h>
24#include <iprt/cpputils.h>
25
26// constructor / destructor
27/////////////////////////////////////////////////////////////////////////////
28
29DEFINE_EMPTY_CTOR_DTOR (SerialPort)
30
31HRESULT SerialPort::FinalConstruct()
32{
33 return S_OK;
34}
35
36void SerialPort::FinalRelease()
37{
38 uninit();
39}
40
41// public initializer/uninitializer for internal purposes only
42/////////////////////////////////////////////////////////////////////////////
43
44/**
45 * Initializes the Serial Port object.
46 *
47 * @param aParent Handle of the parent object.
48 */
49HRESULT SerialPort::init (Machine *aParent, ULONG aSlot)
50{
51 LogFlowThisFunc (("aParent=%p, aSlot=%d\n", aParent, aSlot));
52
53 ComAssertRet (aParent, E_INVALIDARG);
54
55 /* Enclose the state transition NotReady->InInit->Ready */
56 AutoInitSpan autoInitSpan (this);
57 AssertReturn (autoInitSpan.isOk(), E_UNEXPECTED);
58
59 unconst (mParent) = aParent;
60 /* mPeer is left null */
61
62 mData.allocate();
63
64 /* initialize data */
65 mData->mSlot = aSlot;
66
67 /* Confirm a successful initialization */
68 autoInitSpan.setSucceeded();
69
70 return S_OK;
71}
72
73/**
74 * Initializes the Serial Port object given another serial port object
75 * (a kind of copy constructor). This object shares data with
76 * the object passed as an argument.
77 *
78 * @note This object must be destroyed before the original object
79 * it shares data with is destroyed.
80 *
81 * @note Locks @a aThat object for reading.
82 */
83HRESULT SerialPort::init (Machine *aParent, SerialPort *aThat)
84{
85 LogFlowThisFunc (("aParent=%p, aThat=%p\n", aParent, aThat));
86
87 ComAssertRet (aParent && aThat, E_INVALIDARG);
88
89 /* Enclose the state transition NotReady->InInit->Ready */
90 AutoInitSpan autoInitSpan (this);
91 AssertReturn (autoInitSpan.isOk(), E_UNEXPECTED);
92
93 unconst (mParent) = aParent;
94 unconst (mPeer) = aThat;
95
96 AutoCaller thatCaller (aThat);
97 AssertComRCReturnRC (thatCaller.rc());
98
99 AutoReaderLock thatLock (aThat);
100 mData.share (aThat->mData);
101
102 /* Confirm a successful initialization */
103 autoInitSpan.setSucceeded();
104
105 return S_OK;
106}
107
108/**
109 * Initializes the guest object given another guest object
110 * (a kind of copy constructor). This object makes a private copy of data
111 * of the original object passed as an argument.
112 *
113 * @note Locks @a aThat object for reading.
114 */
115HRESULT SerialPort::initCopy (Machine *aParent, SerialPort *aThat)
116{
117 LogFlowThisFunc (("aParent=%p, aThat=%p\n", aParent, aThat));
118
119 ComAssertRet (aParent && aThat, E_INVALIDARG);
120
121 /* Enclose the state transition NotReady->InInit->Ready */
122 AutoInitSpan autoInitSpan (this);
123 AssertReturn (autoInitSpan.isOk(), E_UNEXPECTED);
124
125 unconst (mParent) = aParent;
126 /* mPeer is left null */
127
128 AutoCaller thatCaller (aThat);
129 AssertComRCReturnRC (thatCaller.rc());
130
131 AutoReaderLock thatLock (aThat);
132 mData.attachCopy (aThat->mData);
133
134 /* Confirm a successful initialization */
135 autoInitSpan.setSucceeded();
136
137 return S_OK;
138}
139
140/**
141 * Uninitializes the instance and sets the ready flag to FALSE.
142 * Called either from FinalRelease() or by the parent when it gets destroyed.
143 */
144void SerialPort::uninit()
145{
146 LogFlowThisFunc (("\n"));
147
148 /* Enclose the state transition Ready->InUninit->NotReady */
149 AutoUninitSpan autoUninitSpan (this);
150 if (autoUninitSpan.uninitDone())
151 return;
152
153 mData.free();
154
155 unconst (mPeer).setNull();
156 unconst (mParent).setNull();
157}
158
159// public methods only for internal purposes
160////////////////////////////////////////////////////////////////////////////////
161
162/**
163 * @note Locks this object for writing.
164 */
165bool SerialPort::rollback()
166{
167 /* sanity */
168 AutoCaller autoCaller (this);
169 AssertComRCReturn (autoCaller.rc(), false);
170
171 AutoLock alock (this);
172
173 bool changed = false;
174
175 if (mData.isBackedUp())
176 {
177 /* we need to check all data to see whether anything will be changed
178 * after rollback */
179 changed = mData.hasActualChanges();
180 mData.rollback();
181 }
182
183 return changed;
184}
185
186/**
187 * @note Locks this object for writing, together with the peer object (also
188 * for writing) if there is one.
189 */
190void SerialPort::commit()
191{
192 /* sanity */
193 AutoCaller autoCaller (this);
194 AssertComRCReturnVoid (autoCaller.rc());
195
196 /* sanity too */
197 AutoCaller thatCaller (mPeer);
198 AssertComRCReturnVoid (thatCaller.rc());
199
200 /* lock both for writing since we modify both */
201 AutoMultiLock <2> alock (this->wlock(), AutoLock::maybeWlock (mPeer));
202
203 if (mData.isBackedUp())
204 {
205 mData.commit();
206 if (mPeer)
207 {
208 /* attach new data to the peer and reshare it */
209 mPeer->mData.attach (mData);
210 }
211 }
212}
213
214/**
215 * @note Locks this object for writing, together with the peer object
216 * represented by @a aThat (locked for reading).
217 */
218void SerialPort::copyFrom (SerialPort *aThat)
219{
220 AssertReturnVoid (aThat != NULL);
221
222 /* sanity */
223 AutoCaller autoCaller (this);
224 AssertComRCReturnVoid (autoCaller.rc());
225
226 /* sanity too */
227 AutoCaller thatCaller (mPeer);
228 AssertComRCReturnVoid (thatCaller.rc());
229
230 /* peer is not modified, lock it for reading */
231 AutoMultiLock <2> alock (this->wlock(), aThat->rlock());
232
233 /* this will back up current data */
234 mData.assignCopy (aThat->mData);
235}
236
237HRESULT SerialPort::loadSettings (CFGNODE aNode, ULONG aSlot)
238{
239 LogFlowThisFunc (("aMachine=%p\n", aNode));
240
241 AssertReturn (aNode, E_FAIL);
242
243 AutoCaller autoCaller (this);
244 AssertComRCReturnRC (autoCaller.rc());
245
246 AutoLock alock (this);
247
248 HRESULT rc = S_OK;
249
250 CFGNODE portNode = NULL;
251 CFGLDRGetChildNode (aNode, "Port", aSlot, &portNode);
252
253 /* slot number (required) */
254 /* slot unicity is guaranteed by XML Schema */
255 uint32_t uSlot = 0;
256 CFGLDRQueryUInt32 (portNode, "slot", &uSlot);
257 /* enabled (required) */
258 bool fEnabled = false;
259 CFGLDRQueryBool (portNode, "enabled", &fEnabled);
260 uint32_t uIOBase;
261 /* I/O base (required) */
262 CFGLDRQueryUInt32 (portNode, "IOBase", &uIOBase);
263 /* IRQ (required) */
264 uint32_t uIRQ;
265 CFGLDRQueryUInt32 (portNode, "IRQ", &uIRQ);
266 /* host mode (required) */
267 Bstr mode;
268 CFGLDRQueryBSTR (portNode, "hostMode", mode.asOutParam());
269 if (mode == L"HostPipe")
270 mData->mHostMode = PortMode_HostPipePort;
271 else if (mode == L"HostDevice")
272 mData->mHostMode = PortMode_HostDevicePort;
273 else
274 mData->mHostMode = PortMode_DisconnectedPort;
275 /* pipe/device path */
276 Bstr path;
277 CFGLDRQueryBSTR (portNode, "path", path.asOutParam());
278 /* server mode */
279 bool fServer = true;
280 CFGLDRQueryBool (portNode, "server", &fServer);
281
282 mData->mEnabled = fEnabled;
283 mData->mSlot = uSlot;
284 mData->mIOBase = uIOBase;
285 mData->mIRQ = uIRQ;
286
287 rc = checkSetPath (path);
288 CheckComRCReturnRC (rc);
289
290 mData->mPath = path;
291 mData->mServer = fServer;
292
293 return rc;
294}
295
296/**
297 * Saves the port settings to the given <Port> node.
298 *
299 * Note that the given node is always empty so it is not necessary to delete
300 * old values.
301 *
302 * @param aNode Node to save the settings to.
303 *
304 * @return
305 */
306HRESULT SerialPort::saveSettings (CFGNODE aNode)
307{
308 AssertReturn (aNode, E_FAIL);
309
310 AutoCaller autoCaller (this);
311 CheckComRCReturnRC (autoCaller.rc());
312
313 AutoReaderLock alock (this);
314
315 CFGNODE portNode = 0;
316 int vrc = CFGLDRAppendChildNode (aNode, "Port", &portNode);
317 ComAssertRCRet (vrc, E_FAIL);
318
319 const char *mode;
320 switch (mData->mHostMode)
321 {
322 default:
323 case PortMode_DisconnectedPort:
324 mode = "Disconnected";
325 break;
326 case PortMode_HostPipePort:
327 mode = "HostPipe";
328 break;
329 case PortMode_HostDevicePort:
330 mode = "HostDevice";
331 break;
332 }
333 CFGLDRSetUInt32 (portNode, "slot", mData->mSlot);
334 CFGLDRSetBool (portNode, "enabled", !!mData->mEnabled);
335 CFGLDRSetUInt32Ex (portNode, "IOBase", mData->mIOBase, 16);
336 CFGLDRSetUInt32 (portNode, "IRQ", mData->mIRQ);
337 CFGLDRSetString (portNode, "hostMode", mode);
338
339 /* Always save non-null mPath and mServer to preserve the user values for
340 * later use. Note that 'server' is false by default in XML so we don't
341 * save it when it's false. */
342 if (!mData->mPath.isEmpty())
343 CFGLDRSetBSTR (portNode, "path", mData->mPath);
344 if (mData->mServer)
345 CFGLDRSetBool (portNode, "server", !!mData->mServer);
346
347 return S_OK;
348}
349
350// ISerialPort properties
351/////////////////////////////////////////////////////////////////////////////
352
353STDMETHODIMP SerialPort::COMGETTER(Enabled) (BOOL *aEnabled)
354{
355 if (!aEnabled)
356 return E_POINTER;
357
358 AutoCaller autoCaller (this);
359 CheckComRCReturnRC (autoCaller.rc());
360
361 AutoReaderLock alock (this);
362
363 *aEnabled = mData->mEnabled;
364
365 return S_OK;
366}
367
368STDMETHODIMP SerialPort::COMSETTER(Enabled) (BOOL aEnabled)
369{
370 LogFlowThisFunc (("aEnabled=%RTbool\n", aEnabled));
371
372 AutoCaller autoCaller (this);
373 CheckComRCReturnRC (autoCaller.rc());
374
375 /* the machine needs to be mutable */
376 Machine::AutoMutableStateDependency adep (mParent);
377 CheckComRCReturnRC (adep.rc());
378
379 AutoLock alock (this);
380
381 if (mData->mEnabled != aEnabled)
382 {
383 mData.backup();
384 mData->mEnabled = aEnabled;
385
386 /* leave the lock before informing callbacks */
387 alock.unlock();
388
389 mParent->onSerialPortChange (this);
390 }
391
392 return S_OK;
393}
394
395STDMETHODIMP SerialPort::COMGETTER(HostMode) (PortMode_T *aHostMode)
396{
397 if (!aHostMode)
398 return E_POINTER;
399
400 AutoCaller autoCaller (this);
401 CheckComRCReturnRC (autoCaller.rc());
402
403 AutoReaderLock alock (this);
404
405 *aHostMode = mData->mHostMode;
406
407 return S_OK;
408}
409
410STDMETHODIMP SerialPort::COMSETTER(HostMode) (PortMode_T aHostMode)
411{
412 AutoCaller autoCaller (this);
413 CheckComRCReturnRC (autoCaller.rc());
414
415 /* the machine needs to be mutable */
416 Machine::AutoMutableStateDependency adep (mParent);
417 CheckComRCReturnRC (adep.rc());
418
419 AutoLock alock (this);
420
421 HRESULT rc = S_OK;
422 bool emitChangeEvent = false;
423
424 if (mData->mHostMode != aHostMode)
425 {
426 switch (aHostMode)
427 {
428 case PortMode_HostPipePort:
429 if (mData->mPath.isEmpty())
430 return setError (E_INVALIDARG,
431 tr ("Cannot set the host pipe mode of the serial port %d "
432 "because the pipe path is empty or null"),
433 mData->mSlot);
434 break;
435 case PortMode_HostDevicePort:
436 if (mData->mPath.isEmpty())
437 return setError (E_INVALIDARG,
438 tr ("Cannot set the host device mode of the serial port %d "
439 "because the device path is empty or null"),
440 mData->mSlot);
441 break;
442 case PortMode_DisconnectedPort:
443 break;
444 }
445
446 mData.backup();
447 mData->mHostMode = aHostMode;
448
449 emitChangeEvent = true;
450 }
451
452 if (emitChangeEvent)
453 {
454 /* leave the lock before informing callbacks */
455 alock.unlock();
456
457 mParent->onSerialPortChange (this);
458 }
459
460 return rc;
461}
462
463STDMETHODIMP SerialPort::COMGETTER(Slot) (ULONG *aSlot)
464{
465 if (!aSlot)
466 return E_POINTER;
467
468 AutoCaller autoCaller (this);
469 CheckComRCReturnRC (autoCaller.rc());
470
471 AutoReaderLock alock (this);
472
473 *aSlot = mData->mSlot;
474
475 return S_OK;
476}
477
478STDMETHODIMP SerialPort::COMGETTER(IRQ) (ULONG *aIRQ)
479{
480 if (!aIRQ)
481 return E_POINTER;
482
483 AutoCaller autoCaller (this);
484 CheckComRCReturnRC (autoCaller.rc());
485
486 AutoReaderLock alock (this);
487
488 *aIRQ = mData->mIRQ;
489
490 return S_OK;
491}
492
493STDMETHODIMP SerialPort::COMSETTER(IRQ)(ULONG aIRQ)
494{
495 /* check IRQ limits
496 * (when changing this, make sure it corresponds to XML schema */
497 if (aIRQ > 255)
498 return setError (E_INVALIDARG,
499 tr ("Invalid IRQ number of the serial port %d: "
500 "%lu (must be in range [0, %lu])"),
501 mData->mSlot, aIRQ, 255);
502
503 AutoCaller autoCaller (this);
504 CheckComRCReturnRC (autoCaller.rc());
505
506 /* the machine needs to be mutable */
507 Machine::AutoMutableStateDependency adep (mParent);
508 CheckComRCReturnRC (adep.rc());
509
510 AutoLock alock (this);
511
512 HRESULT rc = S_OK;
513 bool emitChangeEvent = false;
514
515 if (mData->mIRQ != aIRQ)
516 {
517 mData.backup();
518 mData->mIRQ = aIRQ;
519 emitChangeEvent = true;
520 }
521
522 if (emitChangeEvent)
523 {
524 /* leave the lock before informing callbacks */
525 alock.unlock();
526
527 mParent->onSerialPortChange (this);
528 }
529
530 return rc;
531}
532
533STDMETHODIMP SerialPort::COMGETTER(IOBase) (ULONG *aIOBase)
534{
535 if (!aIOBase)
536 return E_POINTER;
537
538 AutoCaller autoCaller (this);
539 CheckComRCReturnRC (autoCaller.rc());
540
541 AutoReaderLock alock (this);
542
543 *aIOBase = mData->mIOBase;
544
545 return S_OK;
546}
547
548STDMETHODIMP SerialPort::COMSETTER(IOBase)(ULONG aIOBase)
549{
550 /* check IOBase limits
551 * (when changing this, make sure it corresponds to XML schema */
552 if (aIOBase > 0xFFFF)
553 return setError (E_INVALIDARG,
554 tr ("Invalid I/O port base address of the serial port %d: "
555 "%lu (must be in range [0, 0x%X])"),
556 mData->mSlot, aIOBase, 0, 0xFFFF);
557
558 AutoCaller autoCaller (this);
559 CheckComRCReturnRC (autoCaller.rc());
560
561 /* the machine needs to be mutable */
562 Machine::AutoMutableStateDependency adep (mParent);
563 CheckComRCReturnRC (adep.rc());
564
565 AutoLock alock (this);
566
567 HRESULT rc = S_OK;
568 bool emitChangeEvent = false;
569
570 if (mData->mIOBase != aIOBase)
571 {
572 mData.backup();
573 mData->mIOBase = aIOBase;
574 emitChangeEvent = true;
575 }
576
577 if (emitChangeEvent)
578 {
579 /* leave the lock before informing callbacks */
580 alock.unlock();
581
582 mParent->onSerialPortChange (this);
583 }
584
585 return rc;
586}
587
588STDMETHODIMP SerialPort::COMGETTER(Path) (BSTR *aPath)
589{
590 if (!aPath)
591 return E_POINTER;
592
593 AutoCaller autoCaller (this);
594 CheckComRCReturnRC (autoCaller.rc());
595
596 AutoReaderLock alock (this);
597
598 mData->mPath.cloneTo (aPath);
599
600 return S_OK;
601}
602
603/**
604 * Validates COMSETTER(Path) arguments.
605 */
606HRESULT SerialPort::checkSetPath (BSTR aPath)
607{
608 AssertReturn (isLockedOnCurrentThread(), E_FAIL);
609
610 if ((mData->mHostMode == PortMode_HostDevicePort ||
611 mData->mHostMode == PortMode_HostPipePort) &&
612 (aPath == NULL || *aPath == '\0'))
613 return setError (E_INVALIDARG,
614 tr ("Path of the serial port %d may not be empty or null in "
615 "host pipe or host device mode"),
616 mData->mSlot);
617
618 return S_OK;
619}
620
621STDMETHODIMP SerialPort::COMSETTER(Path) (INPTR BSTR aPath)
622{
623 if (!aPath)
624 return E_POINTER;
625
626 AutoCaller autoCaller (this);
627 CheckComRCReturnRC (autoCaller.rc());
628
629 /* the machine needs to be mutable */
630 Machine::AutoMutableStateDependency adep (mParent);
631 CheckComRCReturnRC (adep.rc());
632
633 AutoLock alock (this);
634
635 if (mData->mPath != aPath)
636 {
637 HRESULT rc = checkSetPath (aPath);
638 CheckComRCReturnRC (rc);
639
640 mData.backup();
641 mData->mPath = aPath;
642
643 /* leave the lock before informing callbacks */
644 alock.unlock();
645
646 return mParent->onSerialPortChange (this);
647 }
648
649 return S_OK;
650}
651
652STDMETHODIMP SerialPort::COMGETTER(Server) (BOOL *aServer)
653{
654 if (!aServer)
655 return E_POINTER;
656
657 AutoCaller autoCaller (this);
658 CheckComRCReturnRC (autoCaller.rc());
659
660 AutoReaderLock alock (this);
661
662 *aServer = mData->mServer;
663
664 return S_OK;
665}
666
667STDMETHODIMP SerialPort::COMSETTER(Server) (BOOL aServer)
668{
669 LogFlowThisFunc (("aServer=%RTbool\n", aServer));
670
671 AutoCaller autoCaller (this);
672 CheckComRCReturnRC (autoCaller.rc());
673
674 /* the machine needs to be mutable */
675 Machine::AutoMutableStateDependency adep (mParent);
676 CheckComRCReturnRC (adep.rc());
677
678 AutoLock alock (this);
679
680 if (mData->mServer != aServer)
681 {
682 mData.backup();
683 mData->mServer = aServer;
684
685 /* leave the lock before informing callbacks */
686 alock.unlock();
687
688 mParent->onSerialPortChange (this);
689 }
690
691 return S_OK;
692}
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