VirtualBox

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

Last change on this file since 20818 was 19624, checked in by vboxsync, 16 years ago

added raw file serial driver

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