VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/DrvAudioVRDE.cpp@ 50819

Last change on this file since 50819 was 50686, checked in by vboxsync, 11 years ago

src/VBox/Devices/Audio, src/VBox/Main/src-client, include/VBox/vmm:

src/VBox/Devices/Audio: part of restructuring of audio code. Devices files correspondin to Hda, AC97 and SB16 audio. The structure of files have been modifed as per PDM specs. The modified code is under #ifdef VBOX_WITH_PDM_AUDIO_DRIVER

src/VBox/Main/src-client: Driver for the VRDE that interacts with DrvAudio. Enhancement of the CFGM tree for audio.

Config.kmk : addition of one configuration parameter that will control whether new audio code is disabled or enabled. "VBOX_WITH_PDM_AUDIO_DRIVER"

pdmaudioifs.h: common header file between Device , Intermediate audio driver and Backends specific to audio.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.1 KB
Line 
1/* $Id: DrvAudioVRDE.cpp 50686 2014-03-04 19:21:18Z vboxsync $ */
2/** @file
3 *
4 * VBox Audio VRDE backend
5 */
6
7/*
8 * Copyright (C) 2006-2010 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18#include "DrvAudioVRDE.h"
19#include "ConsoleImpl.h"
20#include "ConsoleVRDPServer.h"
21
22#include "Logging.h"
23
24#include <VBox/vmm/pdmaudioifs.h>
25#include <VBox/vmm/pdmdrv.h>
26#include <VBox/RemoteDesktop/VRDE.h>
27#include <VBox/vmm/cfgm.h>
28#include <VBox/err.h>
29#include <iprt/mem.h>
30#include <iprt/cdefs.h>
31
32
33/*******************************************************************************
34 *
35 * IO Ring Buffer section
36 *
37 ******************************************************************************/
38
39/* Implementation of a lock free ring buffer which could be used in a multi
40 * threaded environment. Note that only the acquire, release and getter
41 * functions are threading aware. So don't use reset if the ring buffer is
42 * still in use. */
43typedef struct IORINGBUFFER
44{
45 /* The current read position in the buffer */
46 uint32_t uReadPos;
47 /* The current write position in the buffer */
48 uint32_t uWritePos;
49 /* How much space of the buffer is currently in use */
50 volatile uint32_t cBufferUsed;
51 /* How big is the buffer */
52 uint32_t cBufSize;
53 /* The buffer itself */
54 char *pBuffer;
55} IORINGBUFFER;
56/* Pointer to an ring buffer structure */
57typedef IORINGBUFFER* PIORINGBUFFER;
58
59PPDMDRVINS gpDrvIns; //@todo handle this bad programming;
60
61static void IORingBufferCreate(PIORINGBUFFER *ppBuffer, uint32_t cSize)
62{
63 PIORINGBUFFER pTmpBuffer;
64
65 AssertPtr(ppBuffer);
66
67 *ppBuffer = NULL;
68 pTmpBuffer = RTMemAllocZ(sizeof(IORINGBUFFER));
69 if (pTmpBuffer)
70 {
71 pTmpBuffer->pBuffer = RTMemAlloc(cSize);
72 if(pTmpBuffer->pBuffer)
73 {
74 pTmpBuffer->cBufSize = cSize;
75 *ppBuffer = pTmpBuffer;
76 }
77 else
78 RTMemFree(pTmpBuffer);
79 }
80}
81
82static void IORingBufferDestroy(PIORINGBUFFER pBuffer)
83{
84 if (pBuffer)
85 {
86 if (pBuffer->pBuffer)
87 RTMemFree(pBuffer->pBuffer);
88 RTMemFree(pBuffer);
89 }
90}
91
92DECL_FORCE_INLINE(void) IORingBufferReset(PIORINGBUFFER pBuffer)
93{
94 AssertPtr(pBuffer);
95
96 pBuffer->uReadPos = 0;
97 pBuffer->uWritePos = 0;
98 pBuffer->cBufferUsed = 0;
99}
100
101DECL_FORCE_INLINE(uint32_t) IORingBufferFree(PIORINGBUFFER pBuffer)
102{
103 AssertPtr(pBuffer);
104 return pBuffer->cBufSize - ASMAtomicReadU32(&pBuffer->cBufferUsed);
105}
106
107DECL_FORCE_INLINE(uint32_t) IORingBufferUsed(PIORINGBUFFER pBuffer)
108{
109 AssertPtr(pBuffer);
110 return ASMAtomicReadU32(&pBuffer->cBufferUsed);
111}
112
113DECL_FORCE_INLINE(uint32_t) IORingBufferSize(PIORINGBUFFER pBuffer)
114{
115 AssertPtr(pBuffer);
116 return pBuffer->cBufSize;
117}
118
119static void IORingBufferAquireReadBlock(PIORINGBUFFER pBuffer, uint32_t cReqSize, char **ppStart, uint32_t *pcSize)
120{
121 uint32_t uUsed = 0;
122 uint32_t uSize = 0;
123
124 AssertPtr(pBuffer);
125
126 *ppStart = 0;
127 *pcSize = 0;
128
129 /* How much is in use? */
130 uUsed = ASMAtomicReadU32(&pBuffer->cBufferUsed);
131 if (uUsed > 0)
132 {
133 /* Get the size out of the requested size, the read block till the end
134 * of the buffer & the currently used size. */
135 uSize = RT_MIN(cReqSize, RT_MIN(pBuffer->cBufSize - pBuffer->uReadPos, uUsed));
136 if (uSize > 0)
137 {
138 /* Return the pointer address which point to the current read
139 * position. */
140 *ppStart = pBuffer->pBuffer + pBuffer->uReadPos;
141 *pcSize = uSize;
142 }
143 }
144}
145
146DECL_FORCE_INLINE(void) IORingBufferReleaseReadBlock(PIORINGBUFFER pBuffer, uint32_t cSize)
147{
148 AssertPtr(pBuffer);
149
150 /* Split at the end of the buffer. */
151 pBuffer->uReadPos = (pBuffer->uReadPos + cSize) % pBuffer->cBufSize;
152 ASMAtomicSubU32(&pBuffer->cBufferUsed, cSize);
153}
154
155static void IORingBufferAquireWriteBlock(PIORINGBUFFER pBuffer, uint32_t cReqSize, char **ppStart, uint32_t *pcSize)
156{
157 uint32_t uFree;
158 uint32_t uSize;
159
160 AssertPtr(pBuffer);
161
162 *ppStart = 0;
163 *pcSize = 0;
164
165 /* How much is free? */
166 uFree = pBuffer->cBufSize - ASMAtomicReadU32(&pBuffer->cBufferUsed);
167 if (uFree > 0)
168 {
169 /* Get the size out of the requested size, the write block till the end
170 * of the buffer & the currently free size. */
171 uSize = RT_MIN(cReqSize, RT_MIN(pBuffer->cBufSize - pBuffer->uWritePos, uFree));
172 if (uSize > 0)
173 {
174 /* Return the pointer address which point to the current write
175 * position. */
176 *ppStart = pBuffer->pBuffer + pBuffer->uWritePos;
177 *pcSize = uSize;
178 }
179 }
180}
181
182DECL_FORCE_INLINE(void) IORingBufferReleaseWriteBlock(PIORINGBUFFER pBuffer, uint32_t cSize)
183{
184 AssertPtr(pBuffer);
185
186 /* Split at the end of the buffer. */
187 pBuffer->uWritePos = (pBuffer->uWritePos + cSize) % pBuffer->cBufSize;
188
189 ASMAtomicAddU32(&pBuffer->cBufferUsed, cSize);
190}
191
192/****************** Ring Buffer Function Ends *****************/
193
194//@todo need to see if they need to move to pdmifs.h
195#define AUDIO_HOST_ENDIANNESS 0
196#define VOICE_ENABLE 1
197#define VOICE_DISABLE 2
198
199
200/* Initialization status indicator used for the recreation of the AudioUnits. */
201#define CA_STATUS_UNINIT UINT32_C(0) /* The device is uninitialized */
202#define CA_STATUS_IN_INIT UINT32_C(1) /* The device is currently initializing */
203#define CA_STATUS_INIT UINT32_C(2) /* The device is initialized */
204#define CA_STATUS_IN_UNINIT UINT32_C(3) /* The device is currently uninitializing */
205
206//@todo move t_sample as a PDM interface
207//typedef struct { int mute; uint32_t r; uint32_t l; } volume_t;
208
209#define INT_MAX 0x7fffffff
210volume_t nominal_volume = {
211 0,
212 INT_MAX,
213 INT_MAX
214};
215
216/* The desired buffer length in milliseconds. Will be the target total stream
217 * latency on newer version of pulse. Apparent latency can be less (or more.)
218 * In case its need to be used. Currently its not used.
219 */
220#if 0
221static struct
222{
223 int buffer_msecs_out;
224 int buffer_msecs_in;
225} confAudioVRDE
226=
227{
228 INIT_FIELD (.buffer_msecs_out = ) 100,
229 INIT_FIELD (.buffer_msecs_in = ) 100,
230};
231#endif
232/**
233 * Audio VRDE driver instance data.
234 *
235 * @extends PDMIAUDIOSNIFFERCONNECTOR
236 */
237typedef struct DRVAUDIOVRDE
238{
239 /** Pointer to audio VRDE object */
240 AudioVRDE *pAudioVRDE;
241 PPDMDRVINS pDrvIns;
242 /** Pointer to the driver instance structure. */
243 PDMIHOSTAUDIO IHostAudioR3;
244 ConsoleVRDPServer *pConsoleVRDPServer;
245 /** Pointer to the DrvAudio port interface that is above it. */
246 PPDMIAUDIOCONNECTOR pUpPort;
247} DRVAUDIOVRDE, *PDRVAUDIOVRDE;
248typedef struct PDMHOSTVOICEOUT PDMHOSTVOICEOUT;
249typedef PDMHOSTVOICEOUT *PPDMHOSTVOICEOUT;
250
251typedef struct VRDEVoice
252{
253 /* Audio and audio details for recording */
254 PDMHOSTVOICEIN pHostVoiceIn;
255 void * pvUserCtx;
256 /* Number of bytes per frame (bitsPerSample * channels) of the actual input format. */
257 uint32_t cBytesPerFrame;
258 /* Frequency of the actual audio format. */
259 uint32_t uFrequency;
260 /* If the actual format frequence differs from the requested format, this is not NULL. */
261 void *rate;
262 /* Temporary buffer for st_sample_t representation of the input audio data. */
263 void *pvSamplesBuffer;
264 /* buffer for bytes of samples (not rate converted) */
265 uint32_t cbSamplesBufferAllocated;
266 /* Temporary buffer for frequency conversion. */
267 void *pvRateBuffer;
268 /* buffer for bytes rate converted samples */
269 uint32_t cbRateBufferAllocated;
270 /* A ring buffer for transferring data to the playback thread */
271 PIORINGBUFFER pRecordedVoiceBuf;
272 t_sample * convAudioDevFmtToStSampl;
273 uint32_t fIsInit;
274 uint32_t status;
275};
276typedef VRDEVoice *PVRDEVoice;
277
278typedef struct VRDEVoiceOut
279{
280 PDMHOSTVOICEOUT pHostVoiceOut;
281 uint64_t old_ticks;
282 uint64_t cSamplesSentPerSec;
283};
284typedef VRDEVoiceOut * PVRDEVoiceOut;
285
286/** Makes a PDRVBLOCK out of a PPDMIBLOCK. */
287#define PDMIHOSTAUDIO_2_DRVAUDIOVRDE(pInterface) ( (PDRVAUDIOVRDE)((uintptr_t)pInterface - RT_OFFSETOF(DRVAUDIOVRDE, IHostAudioR3)) )
288
289AudioVRDE::AudioVRDE(Console *console)
290 : mpDrv(NULL),
291 mParent(console)
292{
293}
294
295AudioVRDE::~AudioVRDE()
296{
297 if (mpDrv)
298 {
299 mpDrv->pAudioVRDE = NULL;
300 mpDrv = NULL;
301 }
302}
303
304PPDMIAUDIOCONNECTOR AudioVRDE::getDrvAudioPort()
305{
306 Assert(mpDrv);
307 return mpDrv->pUpPort;
308}
309
310void AudioVRDE::handleVRDESvrCmdAudioInputIntercept(bool fIntercept)
311{
312 LogFlow(("AudioVRDE: handleVRDPCmdInputIntercept\n"));
313}
314
315static DECLCALLBACK(void *) drvAudioVRDEInit(PPDMIHOSTAUDIO pInterface)
316{
317 LogFlow(("drvAudioVRDEInit \n"));
318 return 1;
319}
320
321static void drvAudioVRDEPcmInitInfo(PDMPCMPROPERTIES * pProps, audsettings_t *as)
322{
323 int bits = 8, sign = 0, shift = 0;
324 LogFlow(("AudioVRDE: PcmInitInfo \n"));
325
326 switch (as->fmt) {
327 case AUD_FMT_S8:
328 sign = 1;
329 case AUD_FMT_U8:
330 break;
331
332 case AUD_FMT_S16:
333 sign = 1;
334 case AUD_FMT_U16:
335 bits = 16;
336 shift = 1;
337 break;
338
339 case AUD_FMT_S32:
340 sign = 1;
341 case AUD_FMT_U32:
342 bits = 32;
343 shift = 2;
344 break;
345 }
346
347 pProps->uFrequency = as->freq;
348 pProps->cBits = bits;
349 pProps->fSigned = sign;
350 pProps->cChannels = as->nchannels;
351 pProps->cShift = (as->nchannels == 2) + shift;
352 pProps->fAlign = (1 << pProps->cShift) - 1;
353 pProps->cbPerSec = pProps->uFrequency << pProps->cShift;
354 pProps->fSwapEndian = (as->endianness != AUDIO_HOST_ENDIANNESS);
355}
356
357/*
358 * Hard voice (playback)
359 */
360static int audio_pcm_hw_find_min_out (PPDMHOSTVOICEOUT hw, int *nb_livep)
361{
362 PPDMGSTVOICEOUT sw;
363 PPDMGSTVOICEOUT pIter;
364 int m = INT_MAX;
365 int nb_live = 0;
366 LogFlow(("Hard Voice Playback \n"));
367
368 RTListForEach(&hw->HeadGstVoiceOut, pIter, PDMGSTVOICEOUT, ListGstVoiceOut)
369 {
370 sw = pIter;
371 if (sw->State.fActive || !sw->State.fEmpty)
372 {
373 m = audio_MIN (m, sw->cSamplesMixed);
374 nb_live += 1;
375 }
376 }
377
378 *nb_livep = nb_live;
379 return m;
380}
381
382int audio_pcm_hw_get_live_out2 (PPDMHOSTVOICEOUT hw, int *nb_live)
383{
384 int smin;
385
386 smin = audio_pcm_hw_find_min_out (hw, nb_live);
387
388 if (!*nb_live) {
389 return 0;
390 }
391 else
392 {
393 int live = smin;
394
395 if (live < 0 || live > hw->cSamples)
396 {
397 LogFlow(("Error: live=%d hw->samples=%d\n", live, hw->cSamples));
398 return 0;
399 }
400 return live;
401 }
402}
403
404
405int audio_pcm_hw_get_live_out (PPDMHOSTVOICEOUT hw)
406{
407 int nb_live;
408 int live;
409
410 live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
411 if (live < 0 || live > hw->cSamples)
412 {
413 LogFlow(("Error: live=%d hw->samples=%d\n", live, hw->cSamples));
414 return 0;
415 }
416 return live;
417}
418
419/*
420 * Hard voice (capture)
421 */
422static int audio_pcm_hw_find_min_in (PPDMHOSTVOICEIN hw)
423{
424 PPDMGSTVOICEIN pIter;
425 int m = hw->cSamplesCaptured;
426
427 RTListForEach(&hw->HeadGstVoiceIn, pIter, PDMGSTVOICEIN, ListGstVoiceIn)
428 {
429 if (pIter->State.fActive)
430 {
431 m = audio_MIN (m, pIter->cHostSamplesAcquired);
432 }
433 }
434 return m;
435}
436
437int audio_pcm_hw_get_live_in (PPDMHOSTVOICEIN hw)
438{
439 int live = hw->cSamplesCaptured - audio_pcm_hw_find_min_in (hw);
440 if (live < 0 || live > hw->cSamples)
441 {
442 LogFlow(("Error: live=%d hw->samples=%d\n", live, hw->cSamples));
443 return 0;
444 }
445 return live;
446}
447
448static inline void *advance (void *p, int incr)
449{
450 uint8_t *d = (uint8_t*)p;
451 return (d + incr);
452}
453
454uint64_t audio_get_ticks_per_sec (void)
455{
456 return PDMDrvHlpTMGetVirtualFreq (gpDrvIns);
457}
458
459uint64_t audio_get_clock (void)
460{
461 return PDMDrvHlpTMGetVirtualTime (gpDrvIns);
462}
463
464void VRDEReallocSampleBuf(PVRDEVoice pVRDEVoice, uint32_t cSamples)
465{
466 uint32_t cbBuffer = cSamples * sizeof(PDMHOSTSTEREOSAMPLE);
467 if (cbBuffer > pVRDEVoice->cbSamplesBufferAllocated)
468 {
469 if (pVRDEVoice->pvSamplesBuffer)
470 {
471 RTMemFree(pVRDEVoice->pvSamplesBuffer);
472 pVRDEVoice->pvSamplesBuffer = NULL;
473 }
474 pVRDEVoice->pvSamplesBuffer = RTMemAlloc(cbBuffer);
475 if (pVRDEVoice->pvSamplesBuffer)
476 pVRDEVoice->cbSamplesBufferAllocated = cbBuffer;
477 else
478 pVRDEVoice->cbSamplesBufferAllocated = 0;
479 }
480
481}
482
483void VRDEReallocRateAdjSampleBuf(PVRDEVoice pVRDEVoice, uint32_t cSamples)
484{
485 uint32_t cbBuffer = cSamples * sizeof(PDMHOSTSTEREOSAMPLE);
486 if (cbBuffer > pVRDEVoice->cbRateBufferAllocated)
487 {
488 RTMemFree(pVRDEVoice->pvRateBuffer);
489 pVRDEVoice->pvRateBuffer = RTMemAlloc(cbBuffer);
490 if (pVRDEVoice->pvRateBuffer)
491 pVRDEVoice->cbRateBufferAllocated = cbBuffer;
492 else
493 pVRDEVoice->cbRateBufferAllocated = 0;
494 }
495}
496
497/*******************************************************************************
498 *
499 * AudioVRDE input section
500 *
501 ******************************************************************************/
502
503/*
504 * Callback to feed audio input buffer. Samples format is be the same as
505 * in the voice. The caller prepares st_sample_t.
506 *
507 * @param cbSamples Size of pvSamples array in bytes.
508 * @param pvSamples Points to an array of samples.
509 *
510 * @return IPRT status code.
511 */
512static int fltRecordingCallback(PVRDEVoice pVRDEVoice, uint32_t cbSamples, const void *pvSamples)
513{
514 int rc = VINF_SUCCESS;
515 uint32_t csAvail = 0;
516 uint32_t csToWrite = 0;
517 uint32_t cbToWrite = 0;
518 uint32_t csWritten = 0;
519 char *pcDst = NULL;
520
521 LogFlow(("audio-filter: fltRecordingCallback\n"));
522
523 Assert((cbSamples % sizeof(PDMHOSTSTEREOSAMPLE)) == 0);
524
525 if (!pVRDEVoice->fIsInit)
526 return VINF_SUCCESS;
527
528 /* If nothing is pending return immediately. */
529 if (cbSamples == 0)
530 return VINF_SUCCESS;
531
532 /* How much space is free in the ring buffer? */
533 PPDMHOSTSTEREOSAMPLE psSrc;
534 csAvail = IORingBufferFree(pVRDEVoice->pRecordedVoiceBuf) / sizeof(PDMHOSTSTEREOSAMPLE); /* bytes -> samples */
535
536 /* How much space is used in the audio buffer. Use the smaller size of the too. */
537 csAvail = RT_MIN(csAvail, cbSamples / sizeof(PDMHOSTSTEREOSAMPLE));
538
539 /* Iterate as long as data is available */
540 while(csWritten < csAvail)
541 {
542 /* How much is left? */
543 csToWrite = csAvail - csWritten;
544 cbToWrite = csToWrite * sizeof(PDMHOSTSTEREOSAMPLE);
545
546 /* Try to acquire the necessary space from the ring buffer. */
547 IORingBufferAquireWriteBlock(pVRDEVoice->pRecordedVoiceBuf, cbToWrite, &pcDst, &cbToWrite);
548
549 /* How much do we get? */
550 csToWrite = cbToWrite / sizeof(PDMHOSTSTEREOSAMPLE);
551
552 /* Break if nothing is free anymore. */
553 if (RT_UNLIKELY(csToWrite == 0))
554 break;
555
556 /* Copy the data from the audio buffer to the ring buffer in PVRDEVoice. */
557 memcpy(pcDst, (uint8_t *)pvSamples + (csWritten * sizeof(PDMHOSTSTEREOSAMPLE)), cbToWrite);
558
559 /* Release the ring buffer, so the main thread could start reading this data. */
560 IORingBufferReleaseWriteBlock(pVRDEVoice->pRecordedVoiceBuf, cbToWrite);
561
562 csWritten += csToWrite;
563 }
564
565 LogFlow(("AudioVRDE: [Input] Finished writing buffer with %RU32 samples (%RU32 bytes)\n",
566 csWritten, csWritten * sizeof(PDMHOSTSTEREOSAMPLE)));
567
568 return rc;
569}
570
571
572STDMETHODIMP AudioVRDE::handleVRDESvrCmdAudioInputEventBegin(void *pvContext, int iSampleHz, int cChannels, int cBits, bool fUnsigned)
573{
574 int bitIdx;
575 PVRDEVoice pVRDEVoice = (PVRDEVoice)pvContext;
576 LogFlow(("AudioVRDE: handleVRDPCmdInputEventBegin\n"));
577 /* Prepare a format convertion for the actually used format. */
578 pVRDEVoice->cBytesPerFrame = ((cBits + 7) / 8) * cChannels;
579 if (cBits == 16)
580 {
581 bitIdx = 1;
582 }
583 else if (cBits == 32)
584 {
585 bitIdx = 2;
586 }
587 else
588 {
589 bitIdx = 0;
590 }
591 //PPDMIAUDIOCONNECTOR pPort = server->mConsole->getAudioVRDE()->getDrvAudioPort();
592 /* Call DrvAudio interface to get the t_sample type conversion function */
593 pVRDEVoice->convAudioDevFmtToStSampl = mpDrv->pUpPort->pfnConvDevFmtToStSample(mpDrv->pUpPort,
594 (cChannels == 2) ? 1 : 0,
595 !fUnsigned, 0, bitIdx
596 );
597 if (pVRDEVoice->convAudioDevFmtToStSampl)
598 {
599 LogFlow(("AudioVRDE: Failed to get the conversion function \n"));
600 }
601 LogFlow(("AudioVRDE: Required freq as requested by VRDP Server = %d\n", iSampleHz));
602 //if (iSampleHz && iSampleHz != pVRDEVoice->pHostVoiceIn.Props.uFrequency)
603 {
604 /* @todo if the above condition is false then pVRDEVoice->uFrequency will remain 0 */
605 pVRDEVoice->rate = mpDrv->pUpPort->pfnPrepareAudioConversion(mpDrv->pUpPort, iSampleHz,
606 pVRDEVoice->pHostVoiceIn.Props.uFrequency);
607 pVRDEVoice->uFrequency = iSampleHz;
608 LogFlow(("AudioVRDE: pVRDEVoice assigned requested freq =%d\n", pVRDEVoice->uFrequency));
609 }
610 return VINF_SUCCESS;
611}
612
613/*
614 * pvContext: pointer to VRDP voice returned by the VRDP server. The is same pointer that we initialized in
615 * drvAudioVRDEDisableEnableIn VOICE_ENABLE case.
616 */
617void AudioVRDE::handleVRDESvrCmdAudioInputEventData(void *pvContext, const void *pvData, uint32_t cbData)
618{
619 PVRDEVoice pVRDEVoice = (PVRDEVoice)pvContext;
620 PPDMHOSTSTEREOSAMPLE pHostStereoSampleBuf; /* target sample buffer */
621 PPDMHOSTSTEREOSAMPLE pConvertedSampleBuf; /* samples adjusted for rate */
622 uint32_t cSamples = cbData / pVRDEVoice->cBytesPerFrame; /* Count of samples */
623 void * pTmpSampleBuf = NULL;
624 uint32_t cConvertedSamples; /* samples adjusted for rate */
625 uint32_t cbSamples; /* count of bytes occupied by samples */
626 uint32_t rc;
627 LogFlow(("AudioVRDE: handleVRDPCmdInputEventData cbData = %d, bytesperfram=%d\n",
628 cbData, pVRDEVoice->cBytesPerFrame));
629
630 VRDEReallocSampleBuf(pVRDEVoice, cSamples);
631 pHostStereoSampleBuf = (PPDMHOSTSTEREOSAMPLE)pVRDEVoice->pvSamplesBuffer;
632 pVRDEVoice->convAudioDevFmtToStSampl(pHostStereoSampleBuf, pvData, cSamples, &nominal_volume);
633
634 /* count of rate adjusted samples */
635 pVRDEVoice->uFrequency = 22100; /* @todo handle this. How pVRDEVoice will get proper value */
636 cConvertedSamples = (cSamples * pVRDEVoice->pHostVoiceIn.Props.uFrequency) / pVRDEVoice->uFrequency;
637 VRDEReallocRateAdjSampleBuf(pVRDEVoice, cConvertedSamples);
638
639 pConvertedSampleBuf = (PPDMHOSTSTEREOSAMPLE)pVRDEVoice->pvRateBuffer;
640
641 if (pConvertedSampleBuf)
642 {
643 uint32_t cSampleSrc = cSamples;
644 uint32_t cSampleDst = cConvertedSamples;
645 mpDrv->pUpPort->pfnDoRateConversion(mpDrv->pUpPort, pVRDEVoice->rate, pHostStereoSampleBuf,
646 pConvertedSampleBuf, &cSampleSrc, &cConvertedSamples);
647 pTmpSampleBuf = pConvertedSampleBuf;
648 cbSamples = cConvertedSamples * sizeof(PDMHOSTSTEREOSAMPLE);
649 }
650
651 if (cbSamples)
652 {
653 rc = fltRecordingCallback(pVRDEVoice, cbSamples, pTmpSampleBuf);
654 }
655}
656
657/*
658 * pvContext: pointer to VRDP voice returned by the VRDP server. The is same pointer that we initialized in
659 * drvAudioVRDEDisableEnableIn VOICE_ENABLE case.
660 */
661void AudioVRDE::handleVRDESvrCmdAudioInputEventEnd(void *pvContext)
662{
663 PVRDEVoice pVRDEVoice = (PVRDEVoice)pvContext;
664 LogFlow(("AudioVRDE: handleVRDPCmdInputEventEnd\n"));
665 /* The caller will not use this context anymore. */
666 if (pVRDEVoice->rate)
667 {
668 mpDrv->pUpPort->pfnEndAudioConversion(mpDrv->pUpPort, pVRDEVoice->rate);
669 }
670
671 if (pVRDEVoice->pvSamplesBuffer)
672 {
673 RTMemFree(pVRDEVoice->pvSamplesBuffer);
674 pVRDEVoice->pvSamplesBuffer = NULL;
675 }
676 if(pVRDEVoice->pvRateBuffer)
677 {
678 RTMemFree(pVRDEVoice->pvRateBuffer);
679 pVRDEVoice->pvRateBuffer = NULL;
680 }
681}
682
683static DECLCALLBACK(int) drvAudioVRDEInitOut(PPDMIHOSTAUDIO pInterface, PPDMHOSTVOICEOUT pHostVoiceOut, audsettings_t *as)
684{
685 PDRVAUDIOVRDE pDrv = PDMIHOSTAUDIO_2_DRVAUDIOVRDE(pInterface);
686 PVRDEVoiceOut pVRDEVoiceOut = (PVRDEVoiceOut)pHostVoiceOut;
687 LogFlow(("DrvAudioVRDEInitOut: audio input begin cShift=%d\n", pHostVoiceOut->Props.cShift));
688 pHostVoiceOut->cSamples = 6174;
689 drvAudioVRDEPcmInitInfo(&pVRDEVoiceOut->pHostVoiceOut.Props, as);
690 return VINF_SUCCESS;
691
692}
693
694static DECLCALLBACK(int) drvAudioVRDEInitIn (PPDMIHOSTAUDIO pInterface, PPDMHOSTVOICEIN pHostVoiceIn, audsettings_t *as)
695{
696 LogFlow(("DrvAudioVRDE: drvAudioVRDEInitIn \n"));
697 PDRVAUDIOVRDE pDrv = PDMIHOSTAUDIO_2_DRVAUDIOVRDE(pInterface);
698 PVRDEVoice pVRDEVoice = (PVRDEVoice)pHostVoiceIn;
699 pHostVoiceIn->cSamples = 6174;
700 drvAudioVRDEPcmInitInfo(&pVRDEVoice->pHostVoiceIn.Props, as);
701 return VINF_SUCCESS;
702}
703
704static DECLCALLBACK(int) drvAudioVRDEPlayIn(PPDMIHOSTAUDIO pInterface, PPDMHOSTVOICEIN pHostVoiceIn)
705{
706 uint32_t cbAvlblRingBuffer = 0;
707 uint32_t cSamplesRingBuffer = 0;
708 uint32_t cSamplesToRead = 0;
709 uint32_t cSamplesRead = 0;
710 uint32_t cbToRead;
711 char *pcSrc;
712 PDMHOSTSTEREOSAMPLE * psDst;
713 //@todo take care of the size of the buffer allocated to pHostVoiceIn
714 PVRDEVoice pVRDEVoice = (PVRDEVoice)pHostVoiceIn;
715 LogFlow(("DrvAudioVRDE: drvAudioVRDEPlayIn \n"));
716
717 /* use this from DrvHostCoreAudio.c */
718 if (ASMAtomicReadU32(&pVRDEVoice->status) != CA_STATUS_INIT)
719 {
720 LogFlow(("AudioVRDE: VRDE voice not initialized \n"));
721 return 0;
722 }
723
724 /* how much space is used in the ring buffer in pRecordedVocieBuf with pAudioVRDE . Bytes-> samples*/
725 cSamplesRingBuffer = IORingBufferUsed(pVRDEVoice->pRecordedVoiceBuf) / sizeof(PDMHOSTSTEREOSAMPLE);
726
727 /* How much space is available in the mix buffer. Use the smaller size of the too. */
728 cSamplesRingBuffer = RT_MIN(cSamplesRingBuffer, (uint32_t)(pVRDEVoice->pHostVoiceIn.cSamples -
729 audio_pcm_hw_get_live_in (&pVRDEVoice->pHostVoiceIn)));
730 LogFlow(("AudioVRDE: [Input] Start reading buffer with %d samples (%d bytes)\n", cSamplesRingBuffer,
731 cSamplesRingBuffer * sizeof(PDMHOSTSTEREOSAMPLE)));
732
733 /* Iterate as long as data is available */
734 while (cSamplesRead < cSamplesRingBuffer)
735 {
736 /* How much is left? Split request at the end of our samples buffer. */
737 cSamplesToRead = RT_MIN(cSamplesRingBuffer - cSamplesRead,
738 (uint32_t)(pVRDEVoice->pHostVoiceIn.cSamples - pVRDEVoice->pHostVoiceIn.offWrite));
739 cbToRead = cSamplesToRead * sizeof(PDMHOSTSTEREOSAMPLE);
740 LogFlow(("AudioVRDE: [Input] Try reading %RU32 samples (%RU32 bytes)\n", cSamplesToRead, cbToRead));
741
742 /* Try to acquire the necessary block from the ring buffer. Remeber in fltRecrodCallback we
743 * we are filling this buffer with the audio data available from VRDP. Here we are reading it
744 */
745 /*todo do I need to introduce a thread to fill the buffer in fltRecordcallback. So that
746 * filling is in separate thread and the reading of that buffer is in separate thread
747 */
748 IORingBufferAquireReadBlock(pVRDEVoice->pRecordedVoiceBuf, cbToRead, &pcSrc, &cbToRead);
749
750 /* How much to we get? */
751 cSamplesToRead = cbToRead / sizeof(PDMHOSTSTEREOSAMPLE);
752 LogFlow(("AuderVRDE: [Input] There are %d samples (%d bytes) available\n", cSamplesToRead, cbToRead));
753
754 /* Break if nothing is used anymore. */
755 if (cSamplesToRead == 0)
756 {
757 LogFlow(("AudioVRDE: Nothing to read \n"));
758 break;
759 }
760
761 /* Copy the data from our ring buffer to the mix buffer. */
762 psDst = pVRDEVoice->pHostVoiceIn.pConversionBuf + pVRDEVoice->pHostVoiceIn.offWrite;
763 memcpy(psDst, pcSrc, cbToRead);
764
765 /* Release the read buffer, so it could be used for new data. */
766 IORingBufferReleaseReadBlock(pVRDEVoice->pRecordedVoiceBuf, cbToRead);
767
768 pVRDEVoice->pHostVoiceIn.offWrite = (pVRDEVoice->pHostVoiceIn.offWrite + cSamplesToRead)
769 % pVRDEVoice->pHostVoiceIn.cSamples;
770
771 /* How much have we reads so far. */
772 cSamplesRead += cSamplesToRead;
773 }
774 LogFlow(("AudioVRDE: [Input] Finished reading buffer with %d samples (%d bytes)\n",
775 cSamplesRead, cSamplesRead * sizeof(PDMHOSTSTEREOSAMPLE)));
776
777 return cSamplesRead;
778}
779
780static DECLCALLBACK(int) drvAudioVRDEPlayOut(PPDMIHOSTAUDIO pInterface, PPDMHOSTVOICEOUT pHostVoiceOut)
781{
782 PDRVAUDIOVRDE pDrv = PDMIHOSTAUDIO_2_DRVAUDIOVRDE(pInterface);
783 PVRDEVoiceOut pVRDEVoiceOut = (PVRDEVoiceOut)pHostVoiceOut;
784 int live;
785 uint8_t *pu8Dst;
786 int cSamplesPlayed;
787 int cSamplesToSend = 0;
788 /*
789 * Just call the VRDP server with the data.
790 */
791 live = audio_pcm_hw_get_live_out (pHostVoiceOut);
792 uint64_t now = audio_get_clock();
793 uint64_t ticks = now - pVRDEVoiceOut->old_ticks;
794 uint64_t ticks_per_second = audio_get_ticks_per_sec();
795 cSamplesPlayed = (int)((2 * ticks * pHostVoiceOut->Props.uFrequency + ticks_per_second) / ticks_per_second / 2);
796 if (cSamplesPlayed < 0)
797 cSamplesPlayed = live;
798 pHostVoiceOut->Props.cBits = 128;
799 VRDEAUDIOFORMAT format = VRDE_AUDIO_FMT_MAKE(pHostVoiceOut->Props.uFrequency,
800 pHostVoiceOut->Props.cChannels,
801 pHostVoiceOut->Props.cBits, /* bits per sample */
802 !pHostVoiceOut->Props.fSigned);
803 LogFlow(("DrvAudioVRDE: send audio sample freq=%d, chan=%d, cBits = %d, fsigned = %d, cSamples=%d format=%d \n",
804 pHostVoiceOut->Props.uFrequency, pHostVoiceOut->Props.cChannels,
805 pHostVoiceOut->Props.cBits, pHostVoiceOut->Props.fSigned,
806 pHostVoiceOut->cSamples, format)
807 );
808 pVRDEVoiceOut->old_ticks = now;
809 cSamplesToSend = RT_MIN(live, cSamplesPlayed);
810 if (pHostVoiceOut->offRead + cSamplesToSend > pHostVoiceOut->cSamples)
811 {
812 /* send the samples till the end of pHostStereoSampleBuf */
813 pDrv->pConsoleVRDPServer->SendAudioSamples(&pHostVoiceOut->pHostSterioSampleBuf[pHostVoiceOut->offRead],
814 (pHostVoiceOut->cSamples - pHostVoiceOut->offRead), format);
815 /*pHostStereoSampleBuff already has the samples which exceeded its space. They have overwriten the old
816 * played sampled starting from offset 0. So based on the number of samples that we had to play,
817 * read the number of samples from offset 0 .
818 */
819 pDrv->pConsoleVRDPServer->SendAudioSamples(&pHostVoiceOut->pHostSterioSampleBuf[0],
820 (cSamplesToSend - (pHostVoiceOut->cSamples -
821 pHostVoiceOut->offRead)),
822 format);
823 }
824 else
825 {
826 pDrv->pConsoleVRDPServer->SendAudioSamples(&pHostVoiceOut->pHostSterioSampleBuf[pHostVoiceOut->offRead],
827 cSamplesToSend, format);
828 }
829 pHostVoiceOut->offRead = (pHostVoiceOut->offRead + cSamplesToSend) % pHostVoiceOut->cSamples;
830 return cSamplesToSend;
831}
832
833static DECLCALLBACK(void) drvAudioVRDEFiniIn(PPDMIHOSTAUDIO pInterface, PPDMHOSTVOICEIN hw)
834{
835 PDRVAUDIOVRDE pDrv = PDMIHOSTAUDIO_2_DRVAUDIOVRDE(pInterface);
836 LogFlow(("DrvAudioVRDE: drvAudioVRDEFiniIn \n"));
837 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
838}
839
840static DECLCALLBACK(void) drvAudioVRDEFiniOut(PPDMIHOSTAUDIO pInterface, PPDMHOSTVOICEOUT pHostVoiceOut)
841{
842 PDRVAUDIOVRDE pDrv = PDMIHOSTAUDIO_2_DRVAUDIOVRDE(pInterface);
843 LogFlow(("DrvAudioVRDE: audio input end\n"));
844}
845
846static DECLCALLBACK(int) drvAudioVRDEDisableEnableOut(PPDMIHOSTAUDIO pInterface, PPDMHOSTVOICEOUT hw, int cmd)
847{
848 LogFlow(("DrvAudioVRDE: drvAudioVRDEDisableEnableOut \n"));
849 return VINF_SUCCESS;
850}
851
852static DECLCALLBACK(int) drvAudioVRDEDisableEnableIn(PPDMIHOSTAUDIO pInterface, PPDMHOSTVOICEIN pHostVoiceIn, int cmd)
853{
854 PDRVAUDIOVRDE pDrv = PDMIHOSTAUDIO_2_DRVAUDIOVRDE(pInterface);
855
856 /* Initialize VRDEVoice and return to VRDP server which returns this struct back to us
857 * in the form void * pvContext
858 */
859 PVRDEVoice pVRDEVoice = (PVRDEVoice)pHostVoiceIn;
860 LogFlow(("DrvAudioVRDE: drvAudioVRDEDisableEnableIn \n"));
861 /* initialize only if not already done */
862 if (cmd == VOICE_ENABLE)
863 {
864 //@todo if (!pVRDEVoice->fIsInit)
865 // IORingBufferReset(pVRDEVoice->pRecordedVoiceBuf);
866 LogFlow(("DrvAudioVRDE: Intializing the VRDE params and buffer \n"));
867 pVRDEVoice->fIsInit = 1;
868 pVRDEVoice->pHostVoiceIn = *pHostVoiceIn;
869 pVRDEVoice->cBytesPerFrame =1 ;
870 pVRDEVoice->uFrequency = 0;
871 pVRDEVoice->rate = NULL;
872 pVRDEVoice->cbSamplesBufferAllocated = 0;
873 pVRDEVoice->pvRateBuffer = NULL;
874 pVRDEVoice->cbRateBufferAllocated = 0;
875
876 pVRDEVoice->pHostVoiceIn.cSamples = 2048;
877 /* Initialize the hardware info section with the audio settings */
878
879 ASMAtomicWriteU32(&pVRDEVoice->status, CA_STATUS_IN_INIT);
880
881 /* Create the internal ring buffer. */
882 IORingBufferCreate(&pVRDEVoice->pRecordedVoiceBuf,
883 pVRDEVoice->pHostVoiceIn.cSamples * sizeof(PDMHOSTSTEREOSAMPLE));
884
885 if (!RT_VALID_PTR(pVRDEVoice->pRecordedVoiceBuf))
886 {
887 LogRel(("AudioVRDE: [Input] Failed to create internal ring buffer\n"));
888 return VERR_NO_MEMORY;
889 }
890
891 ASMAtomicWriteU32(&pVRDEVoice->status, CA_STATUS_INIT);
892 return pDrv->pConsoleVRDPServer->SendAudioInputBegin(NULL, pVRDEVoice, pHostVoiceIn->cSamples,
893 pHostVoiceIn->Props.uFrequency,
894 pHostVoiceIn->Props.cChannels, pHostVoiceIn->Props.cBits);
895 }
896 else if (cmd == VOICE_DISABLE)
897 {
898 LogFlow(("DrvAudioVRDE: Cmd to disable VRDE \n"));
899 pVRDEVoice->fIsInit = 0;
900 ASMAtomicWriteU32(&pVRDEVoice->status, CA_STATUS_IN_UNINIT);
901 IORingBufferDestroy(pVRDEVoice->pRecordedVoiceBuf);
902 pVRDEVoice->pRecordedVoiceBuf = NULL;
903 ASMAtomicWriteU32(&pVRDEVoice->status, CA_STATUS_UNINIT);
904 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
905 }
906 return VINF_SUCCESS;
907}
908
909static DECLCALLBACK(void) drvAudioVRDEGetConf(PPDMIBASE pInterface, PPDMAUDIOCONF pAudioConf)
910{
911 LogFlow(("drvAudioVRDE: drvAudioVRDEGetConf \n"));
912 /* @todo check if szHostVoiceOut = sizeof VRDEVoice works. VRDEVoice doesn't contain HOSTVOICEOUT. */
913 pAudioConf->szHostVoiceOut = sizeof(VRDEVoice);
914 pAudioConf->szHostVoiceIn = sizeof(VRDEVoice);
915 pAudioConf->MaxHostVoicesOut = 1;
916 pAudioConf->MaxHostVoicesIn = 1;
917}
918
919/**
920 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
921 */
922static DECLCALLBACK(void *) drvAudioVRDEQueryInterface(PPDMIBASE pInterface, const char *pszIID)
923{
924 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
925 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
926 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
927 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudioR3);
928 return NULL;
929}
930
931
932static DECLCALLBACK(void) drvAudioVRDEDestruct(PPDMDRVINS pDrvIns)
933{
934}
935
936/**
937 * Construct a DirectSound Audio driver instance.
938 *
939 * @copydoc FNPDMDRVCONSTRUCT
940 */
941DECLCALLBACK(int) AudioVRDE::drvAudioVRDEConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
942{
943 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
944 LogRel(("drvAudioVRDEConstruct\n"));
945
946 /* we save the address of AudioVRDE in Object node in CFGM tree and address of VRDP server in
947 * ObjectVRDPServer node. So presence of both is necessary.
948 */
949 //if (!CFGMR3AreValuesValid(pCfg, "Object\0") || !CFGMR3AreValuesValid(pCfg, "ObjectVRDPServer\0"))
950 // return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
951 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
952 ("Configuration error: Not possible to attach anything to this driver!\n"),
953 VERR_PDM_DRVINS_NO_ATTACH);
954
955 /*
956 * Init the static parts.
957 */
958 pThis->pDrvIns = pDrvIns;
959 gpDrvIns = pDrvIns;
960 /* IBase */
961 pDrvIns->IBase.pfnQueryInterface = drvAudioVRDEQueryInterface;
962 pThis->IHostAudioR3.pfnInitIn = drvAudioVRDEInitIn;
963 pThis->IHostAudioR3.pfnInitOut = drvAudioVRDEInitOut;
964 pThis->IHostAudioR3.pfnDisableEnableOut = drvAudioVRDEDisableEnableOut;
965 pThis->IHostAudioR3.pfnDisableEnableIn = drvAudioVRDEDisableEnableIn;
966 pThis->IHostAudioR3.pfnFiniIn = drvAudioVRDEFiniIn;
967 pThis->IHostAudioR3.pfnFiniOut = drvAudioVRDEFiniOut;
968 pThis->IHostAudioR3.pfnPlayIn = drvAudioVRDEPlayIn;
969 pThis->IHostAudioR3.pfnPlayOut = drvAudioVRDEPlayOut;
970 pThis->IHostAudioR3.pfnGetConf = drvAudioVRDEGetConf;
971 pThis->IHostAudioR3.pfnInit = drvAudioVRDEInit;
972
973 /* Get VRDPServer pointer */
974 void *pv;
975 int rc = CFGMR3QueryPtr(pCfg, "ObjectVRDPServer", &pv);
976 if (RT_FAILURE(rc))
977 {
978 AssertMsgFailed(("DrvAudioVRDE Confguration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
979 return rc;
980 }
981 /* CFGM tree saves the pointer to ConsoleVRDPServer in the Object node of AudioVRDE */
982 pThis->pConsoleVRDPServer = (ConsoleVRDPServer *)pv;
983 pv = NULL;
984
985 rc = CFGMR3QueryPtr(pCfg, "Object", &pv);
986 if (RT_FAILURE(rc))
987 {
988 AssertMsgFailed(("DrvAudioVRDE Confguration error: No/bad \"Object\" value! rc=%Rrc\n", rc));
989 return rc;
990 }
991 pThis->pAudioVRDE = (AudioVRDE *)pv;
992 pThis->pAudioVRDE->mpDrv = pThis;
993 /*
994 * Get the interface for the above driver (DrvAudio) to make mixer/conversion calls .
995 * Described in CFGM tree.
996 */
997 pThis->pUpPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR);
998 if (!pThis->pUpPort)
999 {
1000 AssertMsgFailed(("Configuration error: No Audio Sniffer port interface above!\n"));
1001 return VERR_PDM_MISSING_INTERFACE_ABOVE;
1002 }
1003
1004 return VINF_SUCCESS;
1005}
1006
1007
1008/**
1009 * Char driver registration record.
1010 */
1011const PDMDRVREG g_DrvAudioVRDE =
1012{
1013 PDM_DRVREG_VERSION,
1014 /* szName */
1015 "AudioVRDE",
1016 /* szRCMod */
1017 "",
1018 /* szR0Mod */
1019 "",
1020 /* pszDescription */
1021 "Audio VRDE",
1022 /* fFlags */
1023 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1024 /* fClass. */
1025 PDM_DRVREG_CLASS_AUDIO,
1026 /* cMaxInstances */
1027 ~0U,
1028 /* cbInstance */
1029 sizeof(DRVAUDIOVRDE),
1030 /* pfnConstruct */
1031 AudioVRDE::drvAudioVRDEConstruct,
1032 /* pfnDestruct */
1033 drvAudioVRDEDestruct,
1034 /* pfnRelocate */
1035 NULL,
1036 /* pfnIOCtl */
1037 NULL,
1038 /* pfnPowerOn */
1039 NULL,
1040 /* pfnReset */
1041 NULL,
1042 /* pfnSuspend */
1043 NULL,
1044 /* pfnResume */
1045 NULL,
1046 /* pfnAttach */
1047 NULL,
1048 /* pfnDetach */
1049 NULL,
1050 /* pfnPowerOff */
1051 NULL,
1052 /* pfnSoftReset */
1053 NULL,
1054 /* u32EndVersion */
1055 PDM_DRVREG_VERSION
1056};
1057
1058
1059
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