/// <summary>
 /// Allocate the memory for the destination buffer and copy the response
 /// to the destination buffer
 /// </summary>
 /// <param name="dstResponse"></param>
 internal void GetResponse(out byte[] dstResponse)
 {
     if (response != null)
     {
         dstResponse = new byte[response.Length];
         Buffer.BlockCopy(response, 0, dstResponse, 0, response.Length);
     }
     else
     {
         ResponsePacketUtil.GenerateResponsePacket(CompletionCode.ResponseNotProvided, out dstResponse);
     }
 }
        /// <summary>
        /// Every component in the higher level of the stack should call this method
        /// to send/receive data to/from actual hardware devices
        /// </summary>
        /// <param name="priorityLevel"></param>
        /// <param name="deviceType"></param>
        /// <param name="deviceId"></param>
        /// <param name="request">
        /// Input
        /// [0]: function code
        /// [1:2]: byte count (N)
        /// [3:N+2]: payload
        /// </param>
        /// <param name="response">
        /// Output
        /// [0]: completion code
        /// [1:2]: byte count (N)
        /// [3:N+2]: payload
        /// Note: response can be null
        /// </param>
        static internal void SendReceive(PriorityLevel priorityLevel, byte deviceType, byte deviceId, byte[] request, out byte[] response)
        {
            Tracer.WriteInfo("CommunicationDevice.SendReceive({0})", deviceType);
            ushort sessionId = IncrementTimeStampAndGetSessionId();
            byte   physicalId;

            // If CM is terminating, do not accept any more new requests
            if (isTerminating == true)
            {
                ResponsePacketUtil.GenerateResponsePacket(CompletionCode.ServiceTerminating, out response);
                return;
            }

            if (IsValidRequest(deviceType, deviceId, ref request) == false)
            {
                ResponsePacketUtil.GenerateResponsePacket(CompletionCode.InvalidCommand, out response);
                return;
            }

            physicalId = GetPhysicalIdFromLogicalId(deviceType, deviceId);
            Tracer.WriteInfo("CommunicationDevice.SendReceive PhysicalID ({0})", physicalId);

            using (WorkItem workItem = new WorkItem(deviceType, physicalId, request, sessionId))
            {
                byte logicalPortId = (byte)LogicalPortId.InvalidLogicalPortId;
                byte functionCode  = RequestPacketUtil.GetFunctionCode(ref request);

                switch (deviceType)
                {
                case (byte)DeviceType.Server:
                // Fall through
                case (byte)DeviceType.BladeConsole:
                    logicalPortId = (byte)LogicalPortId.SerialPortServers;
                    break;

                case (byte)DeviceType.SerialPortConsole:
                    // TODO: Extend the code for more serial consoles
                    if (deviceId == 1)
                    {
                        logicalPortId = (byte)LogicalPortId.SerialPortConsole1;
                    }
                    else if (deviceId == 2)
                    {
                        logicalPortId = (byte)LogicalPortId.SerialPortConsole2;
                    }
                    else if (deviceId == 3)
                    {
                        logicalPortId = (byte)LogicalPortId.SerialPortConsole3;
                    }
                    else if (deviceId == 4)
                    {
                        logicalPortId = (byte)LogicalPortId.SerialPortConsole4;
                    }
                    break;

                default:
                    logicalPortId = (byte)LogicalPortId.SerialPortOtherDevices;
                    break;
                }

                if (logicalPortId == (byte)LogicalPortId.InvalidLogicalPortId)
                {
                    ResponsePacketUtil.GenerateResponsePacket(CompletionCode.InvalidCommand, out response);
                    return;
                }

                // If currently in safe mode, reject all the requests routed to COM4 except for BladeConsole commands
                if ((isSafeModeEnabled == true) &&
                    (logicalPortId == (byte)LogicalPortId.SerialPortServers) &&
                    (deviceType != (byte)DeviceType.BladeConsole))
                {
                    ResponsePacketUtil.GenerateResponsePacket(CompletionCode.CannotExecuteRequestInSafeMode, out response);
                    return;
                }

                if (portManagers[logicalPortId].SendReceive(priorityLevel, workItem) == true)
                {
                    // Successfully added the request in the work queue
                    Tracer.WriteInfo("[logicalPortId: {0}, priorityLevel: {1}] SendReceive succeeded and wait", logicalPortId, priorityLevel);

                    // Sleep until signaled by the device worker thread
                    // Wait time: wait time in the queue + worker thread processing time
                    workItem.Wait();

                    // Copy the response to the output buffer
                    workItem.GetResponse(out response);
                }
                else
                {
                    // Could not add the reuqest in the work queue
                    Tracer.WriteWarning("[logicalPortId: {0}, priorityLevel: {1}] SendReceive failed", logicalPortId, priorityLevel);
                    ResponsePacketUtil.GenerateResponsePacket(CompletionCode.OutOfSpace, out response);
                }
            }
        }