You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
489 lines
21 KiB
489 lines
21 KiB
/****************************************************************************/
|
|
// aupint.cpp
|
|
//
|
|
// RDP Update Packager internal functions.
|
|
//
|
|
// Copyright (C) 1997-2000 Microsoft Corporation
|
|
/****************************************************************************/
|
|
|
|
#include <precomp.h>
|
|
#pragma hdrstop
|
|
|
|
#define TRC_FILE "aupint"
|
|
#include <as_conf.hpp>
|
|
#include <nprcount.h>
|
|
|
|
|
|
/****************************************************************************/
|
|
// UPSendOrders
|
|
//
|
|
// Packages orders to send to the client. Returns TRUE if all orders for
|
|
// this call (all available orders for non-shadow, one buffer for shadow)
|
|
// were sent.
|
|
//
|
|
// Packing algorithm details:
|
|
//
|
|
// Each network buffer (allocated in SC) is size sc8KOutBufUsableSpace
|
|
// (8K minus some header space). Within that space we try to pack to
|
|
// multiples of the 1460-byte TCP payload. For slow link connections we aim
|
|
// for 1460 * 1 (SMALL_SLOWLINK_PAYLOAD_SIZE) as the final size to send,
|
|
// for LAN, 1460 * 3 (LARGE_SLOWLINK_PAYLOAD_SIZE).
|
|
//
|
|
// Order packing takes into account the current MPPC compression estimate
|
|
// for slow links. We divide the target size by the compression ratio to get
|
|
// the predicted size of data that, when compressed, will fit into the
|
|
// target size. This size is throttled by the sc8KOutBufUsableSpace
|
|
// size so we don't overrun the network buffer.
|
|
/****************************************************************************/
|
|
NTSTATUS RDPCALL SHCLASS UPSendOrders(PPDU_PACKAGE_INFO pPkgInfo)
|
|
{
|
|
BYTE *pOrderBuffer;
|
|
unsigned NumOrders;
|
|
unsigned cbOrderBytes;
|
|
unsigned cbOrderBytesRemaining;
|
|
unsigned cbPacketSize;
|
|
int RealSpaceAvail, ScaledSpaceAvail;
|
|
unsigned SmallPackingSize = m_pShm->sch.SmallPackingSize;
|
|
unsigned LargePackingSize = m_pShm->sch.LargePackingSize;
|
|
unsigned MPPCCompEst = m_pShm->sch.MPPCCompressionEst;
|
|
unsigned ScaledSmallPackingSize;
|
|
unsigned ScaledLargePackingSize;
|
|
unsigned CurrentScaledLargePackingSize;
|
|
unsigned BufLen;
|
|
BOOL bSmallPackingSizeTarget;
|
|
#ifdef DC_HICOLOR
|
|
BOOL bTriedVeryLargeBuffer = FALSE;
|
|
#endif
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
|
|
DC_BEGIN_FN("UPSendOrders");
|
|
|
|
// Find out how many bytes of orders there are in the Order List.
|
|
cbOrderBytesRemaining = OA_GetTotalOrderListBytes();
|
|
|
|
// Process any orders on the list.
|
|
if (cbOrderBytesRemaining > 0) {
|
|
TRC_DBG((TB, "%u order bytes to fetch", cbOrderBytesRemaining));
|
|
|
|
BufLen = pPkgInfo->cbLen;
|
|
|
|
// Handling for the first buffer is different from any later ones.
|
|
// We want at least a few bytes beyond the update-orders PDU
|
|
// header for some orders. If we are at the end of a packing buffer
|
|
// for the first packing size, use the second packing size. If
|
|
// we are at the end of the second packing size, we need to flush.
|
|
if (pPkgInfo->cbInUse < SmallPackingSize) {
|
|
// Check that we actually have a buffer allocated.
|
|
if (BufLen) {
|
|
RealSpaceAvail = SmallPackingSize - pPkgInfo->cbInUse;
|
|
if (RealSpaceAvail >=
|
|
(int)(upUpdateHdrSize + SCH_MIN_ORDER_BUFFER_SPACE)) {
|
|
bSmallPackingSizeTarget = TRUE;
|
|
}
|
|
else {
|
|
bSmallPackingSizeTarget = FALSE;
|
|
RealSpaceAvail = LargePackingSize - pPkgInfo->cbInUse;
|
|
}
|
|
pOrderBuffer = (BYTE *)pPkgInfo->pBuffer +
|
|
pPkgInfo->cbInUse + upUpdateHdrSize;
|
|
}
|
|
else {
|
|
goto ForceFlush;
|
|
}
|
|
}
|
|
else {
|
|
// Note RealSpaceAvail is an int to easily handle where cbInUse >
|
|
// LargePackingSize.
|
|
RealSpaceAvail = (int)LargePackingSize - (int)pPkgInfo->cbInUse;
|
|
if (RealSpaceAvail >=
|
|
(int)(upUpdateHdrSize + SCH_MIN_ORDER_BUFFER_SPACE)) {
|
|
bSmallPackingSizeTarget = FALSE;
|
|
pOrderBuffer = (BYTE *)pPkgInfo->pBuffer +
|
|
pPkgInfo->cbInUse + upUpdateHdrSize;
|
|
}
|
|
else {
|
|
ForceFlush:
|
|
status = SC_FlushAndAllocPackage(pPkgInfo);
|
|
|
|
if ( STATUS_SUCCESS == status ) {
|
|
// If we are not shadowing (or are shadowing but had
|
|
// a null buffer in the package), then we can continue.
|
|
// Otherwise, we've sent our one buffer allowed this
|
|
// round.
|
|
if (m_pTSWd->shadowState == SHADOW_NONE || BufLen == 0) {
|
|
TRC_ASSERT((pPkgInfo->cbLen >= LargePackingSize),
|
|
(TB,"Assumed default package alloc size too "
|
|
"small"));
|
|
RealSpaceAvail = (int)SmallPackingSize;
|
|
bSmallPackingSizeTarget = TRUE;
|
|
pOrderBuffer = (BYTE *)pPkgInfo->pBuffer +
|
|
upUpdateHdrSize;
|
|
}
|
|
else {
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
else {
|
|
// Failed to allocate a packet. We skip out immediately
|
|
// and try again later.
|
|
TRC_NRM((TB, "Failed to alloc order packet"));
|
|
INC_INCOUNTER(IN_SND_NO_BUFFER);
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Calculate the scaled packing sizes, which are the sizes of buffer
|
|
// available after the update order PDU header.
|
|
if (m_pTSWd->bCompress) {
|
|
// Whatever size we're packing for, we need to divide by the MPPC
|
|
// compression estimate to get the size we really want to pack
|
|
// so that, after compression, we achieve the size we would like
|
|
// to get. Note we add in a 7/8 fudge factor to increase the
|
|
// chance we will pack within the target buffer and closer to
|
|
// a full packet size. Also tried 3/4, 4/5, and 15/16 as factors
|
|
// but they yielded more frames. The scaled size is throttled at
|
|
// the full size of the buffer.
|
|
ScaledSpaceAvail = (int)(((unsigned)RealSpaceAvail -
|
|
upUpdateHdrSize) * SCH_UNCOMP_BYTES / MPPCCompEst *
|
|
7 / 8);
|
|
ScaledSpaceAvail = min(ScaledSpaceAvail, (int)(pPkgInfo->cbLen -
|
|
pPkgInfo->cbInUse - upUpdateHdrSize));
|
|
|
|
// Calculate the large packing size target for the first buffer,
|
|
// based on the space currently available. This value will be
|
|
// used below for if we need to retry the order copy after
|
|
// failure to transfer any orders to a small buffer size.
|
|
// It is throttled at the buffer maximum size.
|
|
TRC_ASSERT(((int)pPkgInfo->cbInUse < LargePackingSize),
|
|
(TB,"At least LargePackingSize in use and we've not "
|
|
"flushed - cbInUse=%u, LargePackingSize=%u",
|
|
pPkgInfo->cbInUse, LargePackingSize));
|
|
TRC_ASSERT((MPPCCompEst <= SCH_UNCOMP_BYTES),
|
|
(TB,"MPPC compression ratio > 1.0!"));
|
|
CurrentScaledLargePackingSize = (LargePackingSize -
|
|
pPkgInfo->cbInUse - upUpdateHdrSize) * SCH_UNCOMP_BYTES /
|
|
MPPCCompEst * 7 / 8;
|
|
CurrentScaledLargePackingSize = min(CurrentScaledLargePackingSize,
|
|
(pPkgInfo->cbLen - pPkgInfo->cbInUse - upUpdateHdrSize));
|
|
|
|
// Precalculate the large and small sizes for the second and later
|
|
// buffers (where pPkgInfo->cbInUse is reset to 0).
|
|
// Throttle the small size to reduce slow-link burstiness.
|
|
ScaledSmallPackingSize = (SmallPackingSize - upUpdateHdrSize) *
|
|
SCH_UNCOMP_BYTES / MPPCCompEst * 7 / 8;
|
|
ScaledSmallPackingSize = min(ScaledSmallPackingSize,
|
|
(sc8KOutBufUsableSpace - upUpdateHdrSize));
|
|
ScaledSmallPackingSize = (unsigned int)min(ScaledSmallPackingSize,
|
|
(2 * SMALL_SLOWLINK_PAYLOAD_SIZE));
|
|
ScaledLargePackingSize = (LargePackingSize - upUpdateHdrSize) *
|
|
SCH_UNCOMP_BYTES / MPPCCompEst * 7 / 8;
|
|
ScaledLargePackingSize = min(ScaledLargePackingSize,
|
|
(sc8KOutBufUsableSpace - upUpdateHdrSize));
|
|
}
|
|
else {
|
|
ScaledSpaceAvail = RealSpaceAvail - upUpdateHdrSize;
|
|
|
|
// Calculate initial large packing size for the first buffer.
|
|
CurrentScaledLargePackingSize = LargePackingSize -
|
|
pPkgInfo->cbInUse - upUpdateHdrSize;
|
|
|
|
// For uncompressed, packing sizes need no scaling.
|
|
ScaledSmallPackingSize = SmallPackingSize - upUpdateHdrSize;
|
|
ScaledLargePackingSize = LargePackingSize - upUpdateHdrSize;
|
|
}
|
|
|
|
// Keep sending packets while there are some orders to do.
|
|
while (cbOrderBytesRemaining > 0) {
|
|
// Loop in case we need to use multiple packing sizes.
|
|
for (;;) {
|
|
// The encoded orders must not exceed the packing buffer
|
|
// bounds.
|
|
TRC_ASSERT(((pPkgInfo->cbInUse + (unsigned)ScaledSpaceAvail +
|
|
upUpdateHdrSize) <= pPkgInfo->cbLen),
|
|
(TB,"Target ScaledSpaceAvail %d exceeds the "
|
|
"encoding buffer - cbInUse=%u, cbLen=%u, "
|
|
"upHdrSize=%u",
|
|
ScaledSpaceAvail, pPkgInfo->cbInUse,
|
|
pPkgInfo->cbLen, upUpdateHdrSize));
|
|
|
|
// Transfer as many orders into the packet as will fit.
|
|
cbOrderBytes = (unsigned)ScaledSpaceAvail;
|
|
cbOrderBytesRemaining = UPFetchOrdersIntoBuffer(
|
|
pOrderBuffer, &NumOrders, &cbOrderBytes);
|
|
|
|
TRC_DBG((TB, "%u bytes fetched into %d byte payload. %u "
|
|
"remain", cbOrderBytes, ScaledSpaceAvail -
|
|
upUpdateHdrSize, cbOrderBytesRemaining));
|
|
|
|
if (cbOrderBytes > 0) {
|
|
// If we had any orders transferred, fill out the header
|
|
// and record the added bytes in the package.
|
|
if (scUseFastPathOutput) {
|
|
*(pOrderBuffer - upUpdateHdrSize) =
|
|
TS_UPDATETYPE_ORDERS |
|
|
scCompressionUsedValue;
|
|
*((PUINT16_UA)(pOrderBuffer - 2)) =
|
|
(UINT16)NumOrders;
|
|
}
|
|
else {
|
|
TS_UPDATE_ORDERS_PDU UNALIGNED *pUpdateOrdersPDU;
|
|
|
|
pUpdateOrdersPDU = (TS_UPDATE_ORDERS_PDU UNALIGNED *)
|
|
(pOrderBuffer - upUpdateHdrSize);
|
|
pUpdateOrdersPDU->shareDataHeader.pduType2 =
|
|
TS_PDUTYPE2_UPDATE;
|
|
pUpdateOrdersPDU->data.updateType =
|
|
TS_UPDATETYPE_ORDERS;
|
|
pUpdateOrdersPDU->data.numberOrders =
|
|
(UINT16)NumOrders;
|
|
}
|
|
|
|
// Add the data we have and allow MPPC compression to
|
|
// take place.
|
|
TRC_DBG((TB, "Send orders pkt. size(%d)", cbOrderBytes));
|
|
SC_AddToPackage(pPkgInfo, (cbOrderBytes + upUpdateHdrSize),
|
|
TRUE);
|
|
|
|
#ifdef DC_HICOLOR
|
|
// Having sent some data, we can again resort to the
|
|
// Very Large Buffer later if we need to
|
|
bTriedVeryLargeBuffer = FALSE;
|
|
#endif
|
|
// No need to try a larger size.
|
|
break;
|
|
}
|
|
else if (bSmallPackingSizeTarget) {
|
|
// Not having any orders transferred is not an error
|
|
// condition if we are working with a buffer target
|
|
// smaller than LargePackingSize -- there may
|
|
// have been a large order (most likely a cache-bitmap
|
|
// secondary order) that would not fit in the space we
|
|
// had in the buffer. Try again with a larger size.
|
|
ScaledSpaceAvail = CurrentScaledLargePackingSize;
|
|
bSmallPackingSizeTarget = FALSE;
|
|
continue;
|
|
}
|
|
else if (pPkgInfo->cbInUse) {
|
|
// This was the first packet and we may not have had
|
|
// enough space for a really large order. Need to force
|
|
// the packet to flush. Jump out of the loop.
|
|
break;
|
|
}
|
|
#ifdef DC_HICOLOR
|
|
else if (!bTriedVeryLargeBuffer) {
|
|
// Last ditch - try the very biggest package we're
|
|
// allowed to send.
|
|
TRC_NRM((TB, "Failed to send order in 8k - try 16k (%d)",
|
|
sc16KOutBufUsableSpace));
|
|
|
|
if (SC_GetSpaceInPackage(pPkgInfo,
|
|
sc16KOutBufUsableSpace)) {
|
|
pOrderBuffer = (BYTE *)pPkgInfo->pBuffer +
|
|
upUpdateHdrSize;
|
|
ScaledSpaceAvail = sc16KOutBufUsableSpace -
|
|
upUpdateHdrSize;
|
|
bTriedVeryLargeBuffer = TRUE;
|
|
}
|
|
else {
|
|
// Failed to allocate a packet. Skip out immediately
|
|
// and try again later.
|
|
TRC_NRM((TB, "Failed to alloc order packet"));
|
|
INC_INCOUNTER(IN_SND_NO_BUFFER);
|
|
status = STATUS_NO_MEMORY;
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
#endif
|
|
else {
|
|
// We're totally out of luck here. See comments immediately
|
|
// above. Return FALSE to simulate a failed allocation.
|
|
TRC_ASSERT((!bSmallPackingSizeTarget &&
|
|
pPkgInfo->cbInUse > 0),
|
|
(TB,"We failed to add an order even with largest "
|
|
"buffer size"));
|
|
status = STATUS_UNSUCCESSFUL; // what's the right error code here
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
// Force flush only if we have more orders to encode. Otherwise,
|
|
// we might be able to fit in more info into the package.
|
|
if (cbOrderBytesRemaining > 0) {
|
|
// Flush the packet.
|
|
|
|
status = SC_FlushAndAllocPackage(pPkgInfo);
|
|
if ( STATUS_SUCCESS == status ) {
|
|
// If we were unable to transfer during the last order
|
|
// flush, use the large size in this new package.
|
|
if (cbOrderBytes) {
|
|
bSmallPackingSizeTarget = TRUE;
|
|
ScaledSpaceAvail = ScaledSmallPackingSize;
|
|
}
|
|
else {
|
|
bSmallPackingSizeTarget = FALSE;
|
|
ScaledSpaceAvail = ScaledLargePackingSize;
|
|
}
|
|
|
|
// No longer the first packet, we can push out to the full
|
|
// large packing size for packet retries.
|
|
CurrentScaledLargePackingSize = ScaledLargePackingSize;
|
|
|
|
pOrderBuffer = (BYTE *)pPkgInfo->pBuffer +
|
|
upUpdateHdrSize;
|
|
}
|
|
else {
|
|
// Failed to allocate a packet. We skip out immediately
|
|
// and try again later.
|
|
TRC_NRM((TB, "Failed to alloc order packet"));
|
|
INC_INCOUNTER(IN_SND_NO_BUFFER);
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
// If we are not shadowing, then send as many buffers as necessary
|
|
if (m_pTSWd->shadowState == SHADOW_NONE)
|
|
continue;
|
|
|
|
// Else return back to the DD if we are in a shadow to force sending
|
|
// one buffer at a time.
|
|
else if (cbOrderBytesRemaining != 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
TRC_DBG((TB, "%d bytes of orders left", cbOrderBytesRemaining));
|
|
if (cbOrderBytesRemaining == 0) {
|
|
TRC_DBG((TB, "No orders left, reset the start of the heap"));
|
|
OA_ResetOrderList();
|
|
}
|
|
else if (m_pTSWd->shadowState == SHADOW_NONE) {
|
|
TRC_ALT((TB, "Shouldn't get here: heap should be empty!"));
|
|
|
|
// shouldn't we assert here?
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
DC_EXIT_POINT:
|
|
DC_END_FN();
|
|
return status;
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
// UPFetchOrdersIntoBuffer
|
|
//
|
|
// Copies as many orders as will fit from the order heap into the given packet
|
|
// space, freeing the order heap space for each order copied. Returns the
|
|
// number of orders copied and the number of bytes of order heap data
|
|
// remaining.
|
|
/****************************************************************************/
|
|
unsigned RDPCALL SHCLASS UPFetchOrdersIntoBuffer(
|
|
PBYTE pBuffer,
|
|
unsigned *pcOrders,
|
|
PUINT pcbBufferSize)
|
|
{
|
|
PINT_ORDER pOrder;
|
|
unsigned FreeBytesInBuffer;
|
|
unsigned OrdersCopied;
|
|
|
|
DC_BEGIN_FN("UPFetchOrdersIntoBuffer");
|
|
|
|
// Initialize the buffer pointer and size.
|
|
FreeBytesInBuffer = *pcbBufferSize;
|
|
|
|
// Keep a count of the number of orders we copy.
|
|
OrdersCopied = 0;
|
|
|
|
// Return as many orders as possible.
|
|
pOrder = OA_GetFirstListOrder();
|
|
TRC_DBG((TB, "First order: %p", pOrder));
|
|
|
|
while (pOrder != NULL) {
|
|
|
|
#if DC_DEBUG
|
|
unsigned sum = 0;
|
|
unsigned i;
|
|
|
|
// Check the order checksum integrity
|
|
for (i = 0; i < pOrder->OrderLength; i++) {
|
|
sum += pOrder->OrderData[i];
|
|
}
|
|
if (pOrder->CheckSum != sum) {
|
|
TRC_ASSERT((FALSE), (TB, "order heap corruption: %p\n", pOrder));
|
|
}
|
|
#endif
|
|
|
|
// All orders are placed in the heap pre-encoded for the wire.
|
|
// We need simply copy the resulting orders into the target buffer.
|
|
if (pOrder->OrderLength <= FreeBytesInBuffer) {
|
|
TRC_DBG((TB,"Copying heap order at hdr addr %p, len %u",
|
|
pOrder, pOrder->OrderLength));
|
|
|
|
memcpy(pBuffer, pOrder->OrderData, pOrder->OrderLength);
|
|
|
|
// Update the buffer pointer past the encoded order and get the
|
|
// next order.
|
|
pBuffer += pOrder->OrderLength;
|
|
FreeBytesInBuffer -= pOrder->OrderLength;
|
|
OrdersCopied++;
|
|
pOrder = OA_RemoveListOrder(pOrder);
|
|
}
|
|
else {
|
|
// The order was too big to fit in this buffer.
|
|
// Exit the loop - this order will go in the next packet.
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Fill in the packet header.
|
|
*pcOrders = OrdersCopied;
|
|
|
|
// Update the buffer size to indicate how much data we have written.
|
|
*pcbBufferSize -= FreeBytesInBuffer;
|
|
|
|
TRC_DBG((TB, "Returned %d orders in %d bytes", OrdersCopied,
|
|
*pcbBufferSize));
|
|
|
|
DC_END_FN();
|
|
return OA_GetTotalOrderListBytes();
|
|
}
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: UPEnumSoundCaps */
|
|
/* */
|
|
/* Purpose: Enumeration function for sound capabilities */
|
|
/* */
|
|
/* Params: locPersonID - persion ID of supplied caps */
|
|
/* pCapabilities - caps */
|
|
/****************************************************************************/
|
|
void CALLBACK SHCLASS UPEnumSoundCaps(
|
|
LOCALPERSONID locPersonID,
|
|
UINT_PTR UserData,
|
|
PTS_CAPABILITYHEADER pCapabilities)
|
|
{
|
|
PTS_SOUND_CAPABILITYSET pSoundCaps =
|
|
(PTS_SOUND_CAPABILITYSET)pCapabilities;
|
|
|
|
DC_BEGIN_FN("UPEnumSoundCaps");
|
|
|
|
DC_IGNORE_PARAMETER(UserData);
|
|
|
|
TRC_ASSERT((pSoundCaps->capabilitySetType == TS_CAPSETTYPE_SOUND),
|
|
(TB,"Caps type not sound"));
|
|
|
|
// We don't want to take our own sound caps into account - we are the
|
|
// server so we don't advertise support for sound PDUs.
|
|
if (SC_LOCAL_PERSON_ID != locPersonID) {
|
|
// If there are no caps or the beep flag isn't set, disable beeps.
|
|
if (pSoundCaps->lengthCapability == 0 ||
|
|
!(pSoundCaps->soundFlags & TS_SOUND_FLAG_BEEPS))
|
|
upCanSendBeep = FALSE;
|
|
}
|
|
|
|
DC_END_FN();
|
|
} /* UPEnumSoundCaps */
|
|
|