/// <summary>
        /// Check if the request is valid with respect to device type, id, and request length
        /// </summary>
        /// <param name="deviceType"></param>
        /// <param name="deviceId"></param>
        /// <param name="request"></param>
        /// <returns></returns>
        static private bool IsValidRequest(byte deviceType, byte deviceId, ref byte[] request)
        {
            if (DeviceTypeChecker.IsValidDeviceType(deviceType) == false)
            {
                Tracer.WriteError("Invalid device type: {0}", deviceType);
                return(false);
            }
            if (DeviceIdChecker.IsValidLogicalDeviceId(deviceType, deviceId) == false)
            {
                Tracer.WriteError("Invalid device ID: {0}", deviceId);
                return(false);
            }
            if (request == null)
            {
                Tracer.WriteError("Null request packet");
                return(false);
            }

            // For server commands, simply pass through.
            // Thus, no need to check the request packet length as long as it is not null
            if (deviceType != (byte)DeviceType.Server)
            {
                if (RequestPacketUtil.IsValidRequestLength(ref request) == false)
                {
                    return(false);
                }
            }
            return(true);
        }
        /// <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);
                }
            }
        }