summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/nvrm/core/common/nvrm_transport.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/nvrm/core/common/nvrm_transport.c')
-rw-r--r--arch/arm/mach-tegra/nvrm/core/common/nvrm_transport.c1655
1 files changed, 1655 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/nvrm/core/common/nvrm_transport.c b/arch/arm/mach-tegra/nvrm/core/common/nvrm_transport.c
new file mode 100644
index 000000000000..ffc4f83196fc
--- /dev/null
+++ b/arch/arm/mach-tegra/nvrm/core/common/nvrm_transport.c
@@ -0,0 +1,1655 @@
+/*
+ * Copyright (c) 2010 NVIDIA Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NVIDIA Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/** @file
+ * @brief <b>NVIDIA Driver Development Kit:
+ * Transport API</b>
+ *
+ * @b Description: This is the implementation of Transport API, which
+ * implements a simple means to pass messages across a port name regardless of
+ * port exist in what processor (on same processor or other processor).
+ */
+
+#include "nvrm_transport.h"
+#include "nvrm_xpc.h"
+#include "nvrm_interrupt.h"
+#include "nvrm_rpc.h"
+#include "nvutil.h"
+#include "nvassert.h"
+#include "nvcommon.h"
+#include "avp.h"
+
+#define LOOPBACK_PROFILE 0
+
+// indices where to save data for the loopback test
+#define LOOP_CPU_SEND_INDEX 0
+#define LOOP_AVP_ISR_INDEX 1
+#define LOOP_AVP_RECV_INDEX 2
+#define LOOP_AVP_SEND_INDEX 3
+#define LOOP_CPU_ISR_INDEX 4
+#define LOOP_CPU_RECV_INDEX 5
+
+#define SEMAPHORE_BASED_MUTUAL_EXCLUSION 0
+
+enum {MAX_INT_FOR_TRANSPORT = 2};
+
+// Interrupt bit index in the interrupt controller relocation table.
+enum {CPU_TRANSPORT_INT_OBE = 1};
+enum {CPU_TRANSPORT_INT_IBF = 0};
+enum {AVP_TRANSPORT_INT_OBE = 0};
+enum {AVP_TRANSPORT_INT_IBF = 1};
+
+// Some constraints parameter to develop the transport APIs.
+
+// Maximum port name length
+enum {MAX_PORT_NAME_LENGTH = 16};
+
+// Maximum possible message length between the ports
+#define MAX_COMMAND_SIZE 16
+
+// Message header size MessageCommand + port Name + message Length (24 Bytes)
+enum {MESSAGE_HEADER_SIZE = 0x20};
+
+// Maximum receive message queue depth
+enum {MAX_MESSAGE_DEPTH = 30};
+
+// Maximum time to wait for the response when open the port.
+enum {MAX_OPEN_TIMEOUT_MS = 200};
+
+// Try to resend the message after this time.
+enum {MESSAGE_RETRY_AFTER_MS = 500 };
+
+// Connection message transfer and response wait timeout.
+enum {MAX_CONNECTION_TIMEOUT_MS = 500 };
+
+
+
+// Transport Commands which uses to do the handshaking and message transfer
+// between the processor. This commands are send to the remote processor
+// when any type if transaction happens.
+typedef enum
+{
+ TransportCmd_None = 0x0,
+
+ // The first transport command from the cpu->avp will inform the
+ // avp of size of the buffer.
+ TransportCmd_SetBufferInfo,
+
+ // Transport command for staring the connection process.
+ TransportCmd_Connect,
+
+ // Transport command for disconnecting the port and deleting the port entry.
+ TransportCmd_Disconnect,
+
+ // Transport command which used for normal message transfer to the port.
+ TransportCmd_Message,
+
+ // When a command requires a response, the value in the command field will
+ // be changed by the called processor here to indicate that the response is ready.
+ TransportCmd_Response,
+
+ TransportCmd_Force32 = 0x7FFFFFFF
+
+} TransportCmd;
+
+
+
+// Ports (endpoint) state.
+typedef enum
+{
+ // Port is opened only.
+ PortState_Open = 0x1,
+
+ // Port is waiting for connection.
+ PortState_Waiting,
+
+ // Port is connected.
+ PortState_Connected,
+
+ // Port has been disconnected from other side. You can pop out messages
+ // but you can't send anymore
+ PortState_Disconnected,
+
+ // Set to destroy when there is someone waiting for a connection, but
+ // and a different thread calls to kill close the port.
+ PortState_Destroy,
+
+ PortState_Force32 = 0x7FFFFFFF
+} PortState;
+
+
+
+// Message list which will be queued in the port receive message queue.
+typedef struct RmReceiveMessageRec
+{
+ // Length of message.
+ NvU32 MessageLength;
+
+ // Fixed size message buffer where the receiving message will be store.
+ NvU8 MessageBuffer[MAX_MESSAGE_LENGTH];
+} RmReceiveMessage;
+
+
+// Combines the information for keeping the received messages to the
+// corresponding ports.
+typedef struct MessageQueueRec
+{
+ // Receive message Q details to receive the message. We make the queue 1 extra bigger than the
+ // requested size, and then we can do lockless updates because only the Recv function modifies
+ // ReadIndex, and only the ISR modifies the WriteIndex
+ RmReceiveMessage *pReceiveMsg;
+
+ volatile NvU16 ReadIndex;
+ volatile NvU16 WriteIndex;
+
+ NvU16 QueueSize;
+
+} MessageQueue;
+
+
+
+// Combines all required information for the transport port.
+// The port information contains the state, recv message q, message depth and
+// message length.
+typedef struct NvRmTransportRec
+{
+ // Name of the port, 1 exra byte for NULL termination
+ char PortName[MAX_PORT_NAME_LENGTH+1];
+
+ // The state of port whether this is open or connected or waiting for
+ // connection.
+ PortState State;
+
+ // Receive message Box which contains the receive messages for this port.
+ MessageQueue RecvMessageQueue;
+
+ // Semaphore which is signal after getting the message for that port.
+ // This is the client passed semaphore.
+ NvOsSemaphoreHandle hOnPushMsgSem;
+
+ // Pointer to the partner port. If the connect is to a remote partner,
+ // then this pointer is NULL
+ NvRmTransportHandle hConnectedPort;
+
+ // If this is a remote connection, this holds the remote ports "name"
+ NvU32 RemotePort;
+
+ // save a copy of the rm handle.
+ NvRmDeviceHandle hRmDevice;
+
+ struct NvRmTransportRec *pNext;
+
+ // unlikely to be used members at the end
+
+ // to be signalled when someone waits for a connector.
+ NvOsSemaphoreHandle hOnConnectSem;
+
+#if LOOPBACK_PROFILE
+ NvBool bLoopTest;
+#endif
+
+} NvRmTransport;
+
+
+
+// Combines the common information for keeping the transport information and
+// sending and receiving the messages.
+typedef struct NvRmPrivPortsRec
+{
+ // Device handle.
+ NvRmDeviceHandle hDevice;
+
+ // List of port names of the open ports in the system.
+ NvRmTransport *pPortHead;
+
+ // Mutex for transport
+ NvOsMutexHandle mutex;
+
+ NvRmMemHandle hMessageMem;
+ void *pTransmitMem;
+ void *pReceiveMem;
+ NvU32 MessageMemPhysAddr;
+
+ NvRmPrivXpcMessageHandle hXpc;
+
+ // if a message comes in, but the receiver's queue is full,
+ // then we don't clear the inbound message to allow another message
+ // and set this flag. We use 2 variables here, so we don't need a lock.
+ volatile NvU8 ReceiveBackPressureOn;
+ NvU8 ReceiveBackPressureOff;
+
+#if LOOPBACK_PROFILE
+ volatile NvU32 *pTimer;
+#endif
+} NvRmPrivPorts;
+
+
+// !!! Fixme, this should be part of the rm handle.
+static NvRmPrivPorts s_TransportInfo;
+
+extern NvU32 NvRmAvpPrivGetUncachedAddress(NvU32 addr);
+
+#define MESSAGE_QUEUE_SIZE_IN_BYTES ( sizeof(RmReceiveMessage) * (MAX_MESSAGE_DEPTH+1) )
+static NvU32 s_RpcAvpQueue[ (MESSAGE_QUEUE_SIZE_IN_BYTES + 3) / 4 ];
+static NvU32 s_RpcCpuQueue[ (MESSAGE_QUEUE_SIZE_IN_BYTES + 3) / 4 ];
+static struct NvRmTransportRec s_RpcAvpPortStruct;
+static struct NvRmTransportRec s_RpcCpuPortStruct;
+
+static NvOsInterruptHandle s_TransportInterruptHandle = NULL;
+
+static NvRmTransportHandle
+FindPort(NvRmDeviceHandle hDevice, char *pPortName);
+
+static NvError
+NvRmPrivTransportSendMessage(NvRmDeviceHandle hDevice, NvU32 *message, NvU32 MessageLength);
+
+static void HandleAVPResetMessage(NvRmDeviceHandle hDevice);
+
+// expect caller to handle mutex
+static char *NvRmPrivTransportUniqueName(void)
+{
+ static char UniqueName[] = "aaaaaaaa+";
+ NvU32 len = 8;
+ NvU32 i;
+
+ // this will roll a new name until we hit zzzz:zzzz
+ // it's not unbounded, but it is a lot of names...
+ // Unique names end in a '+' which won't be allowed in supplied names, to avoid
+ // collision.
+ for (i=0; i < len; ++i)
+ {
+ ++UniqueName[i];
+ if (UniqueName[i] != 'z')
+ {
+ break;
+ }
+ UniqueName[i] = 'a';
+
+ }
+
+ return UniqueName;
+}
+
+
+/* Returns NV_TRUE if the message was inserted ok
+ * Returns NV_FALSE if message was not inserted because the queue is already full
+
+ */static NvBool
+InsertMessage(NvRmTransportHandle hPort, const NvU8 *message, const NvU32 MessageSize)
+{
+ NvU32 index;
+ NvU32 NextIndex;
+
+ index = (NvU32)hPort->RecvMessageQueue.WriteIndex;
+ NextIndex = index + 1;
+ if (NextIndex == hPort->RecvMessageQueue.QueueSize)
+ NextIndex = 0;
+
+ // check for full condition
+ if (NextIndex == hPort->RecvMessageQueue.ReadIndex)
+ return NV_FALSE;
+
+ // copy in the message
+ NvOsMemcpy(hPort->RecvMessageQueue.pReceiveMsg[index].MessageBuffer,
+ message,
+ MessageSize);
+ hPort->RecvMessageQueue.pReceiveMsg[index].MessageLength = MessageSize;
+
+ hPort->RecvMessageQueue.WriteIndex = (NvU16)NextIndex;
+ return NV_TRUE;
+}
+
+
+static void
+ExtractMessage(NvRmTransportHandle hPort, NvU8 *message, NvU32 *pMessageSize, NvU32 MaxSize)
+{
+ NvU32 NextIndex;
+ NvU32 index = (NvU32)hPort->RecvMessageQueue.ReadIndex;
+ NvU32 size = hPort->RecvMessageQueue.pReceiveMsg[index].MessageLength;
+
+ NextIndex = index + 1;
+ if (NextIndex == hPort->RecvMessageQueue.QueueSize)
+ NextIndex = 0;
+
+ NV_ASSERT(index != hPort->RecvMessageQueue.WriteIndex); // assert on empty condition
+ NV_ASSERT(size <= MaxSize);
+
+ *pMessageSize = size;
+
+ // only do the copy and update if there is sufficient room, otherwise
+ // the caller will propogate an error up.
+ if (size > MaxSize)
+ {
+ return;
+ }
+ NvOsMemcpy(message,
+ hPort->RecvMessageQueue.pReceiveMsg[index].MessageBuffer,
+ size);
+
+ hPort->RecvMessageQueue.ReadIndex = (NvU16)NextIndex;
+}
+
+
+
+static void *s_TmpIsrMsgBuffer;
+
+/**
+ * Connect message
+ * [ Transport Command ]
+ * [ Remote Handle ]
+ * [ Port Name ]
+ *
+ * Response:
+ * [ Remote Handle ] <- [ Local Handle ]
+ */
+
+static void
+HandleConnectMessage(NvRmDeviceHandle hDevice, volatile NvU32 *pMessage)
+{
+ char PortName[MAX_PORT_NAME_LENGTH+1];
+ NvU32 RemotePort;
+ NvRmTransportHandle hPort;
+
+ RemotePort = pMessage[1];
+ NvOsMemcpy(PortName, (void*)&pMessage[2], MAX_PORT_NAME_LENGTH);
+ PortName[MAX_PORT_NAME_LENGTH] = 0;
+
+ // See if there is a local port with that name
+ hPort = FindPort(hDevice, PortName);
+ if (hPort && hPort->State == PortState_Waiting)
+ {
+ NvOsAtomicCompareExchange32((NvS32 *)&hPort->State, PortState_Waiting, PortState_Connected);
+ if (hPort->State == PortState_Connected)
+ {
+ hPort->RemotePort = RemotePort;
+ NvOsSemaphoreSignal(hPort->hOnConnectSem);
+ pMessage[1] = (NvU32)hPort;
+ }
+ else
+ {
+ pMessage[1] = 0;
+ }
+ }
+ else
+ {
+ pMessage[1] = 0;
+ }
+ pMessage[0] = TransportCmd_Response;
+}
+
+
+
+/**
+ * Disconnect message
+ * [ Transport Command ]
+ * [ Local Handle ]
+ *
+ * Response:
+ * [ Local Handle ] <- 0
+ */
+static void
+HandleDisconnectMessage(NvRmDeviceHandle hDevice, volatile NvU32 *pMessage)
+{
+ NvRmTransportHandle hPort;
+ hPort = (NvRmTransportHandle)pMessage[1];
+
+ // !!! For sanity we should walk the list of open ports to make sure this is a valid port!
+ if (hPort && hPort->State == PortState_Connected)
+ {
+ hPort->State = PortState_Disconnected;
+ hPort->RemotePort = 0;
+ }
+ pMessage[1] = 0;
+ pMessage[0] = TransportCmd_None;
+}
+
+
+/**
+ * Disconnect message
+ * [ Transport Command ]
+ * [ Local Handle ]
+ * [ Message Length ]
+ * [ Message ]
+ *
+ * Response:
+ * [ Message Length ] <- NvSuccess
+ * [ Transport Command ] <- When we can accept a new message
+ */
+
+static void
+HandlePortMessage(NvRmDeviceHandle hDevice, volatile NvU32 *pMessage)
+{
+ NvRmTransportHandle hPort;
+ NvU32 MessageLength;
+ NvBool bSuccess;
+
+ hPort = (NvRmTransportHandle)pMessage[1];
+ MessageLength = pMessage[2];
+
+#if LOOPBACK_PROFILE
+ if (hPort && hPort->bLoopTest)
+ {
+# if NV_IS_AVP
+ pMessage[LOOP_AVP_ISR_INDEX + 3] = *s_TransportInfo.pTimer;
+# else
+ pMessage[LOOP_CPU_ISR_INDEX + 3] = *s_TransportInfo.pTimer;
+# endif
+ }
+#endif
+
+
+ // !!! For sanity we should walk the list of open ports to make sure this is a valid port!
+ // Queue the message even if in the open state as presumably this should only have happened if
+ // due to a race condition with the transport connected messages.
+ if (hPort && (hPort->State == PortState_Connected || hPort->State == PortState_Open))
+ {
+ bSuccess = InsertMessage(hPort, (NvU8*)&pMessage[3], MessageLength);
+ if (bSuccess)
+ {
+ if (hPort->hOnPushMsgSem)
+ NvOsSemaphoreSignal(hPort->hOnPushMsgSem);
+ pMessage[0] = TransportCmd_None;
+ }
+ else
+ {
+ ++s_TransportInfo.ReceiveBackPressureOn;
+ }
+ }
+}
+
+static void
+HandleAVPResetMessage(NvRmDeviceHandle hDevice)
+{
+ NvRmTransportHandle hPort;
+
+ hPort = FindPort(hDevice,(char*)"RPC_CPU_PORT");
+ if (hPort && (hPort->State == PortState_Connected || hPort->State == PortState_Open))
+ {
+ NvU32 message;
+ message = NvRmMsg_AVP_Reset;
+ InsertMessage(hPort, (NvU8*)&message, sizeof(NvU32));
+ if (hPort->hOnPushMsgSem)
+ NvOsSemaphoreSignal(hPort->hOnPushMsgSem);
+ else
+ NV_ASSERT(0);
+ }
+ else
+ NV_ASSERT(0);
+
+}
+
+
+/**
+ * Handle the Inbox full interrupt.
+ */
+static void
+InboxFullIsr(void *args)
+{
+ NvRmDeviceHandle hDevice = (NvRmDeviceHandle)args;
+ NvU32 MessageData;
+ NvU32 MessageCommand;
+ volatile NvU32 *pMessage;
+
+ MessageData = NvRmPrivXpcGetMessage(s_TransportInfo.hXpc);
+ if(MessageData == AVP_WDT_RESET)
+ {
+ HandleAVPResetMessage(hDevice);
+ NvRmInterruptDone(s_TransportInterruptHandle);
+ return;
+ }
+ // if we're on the AVP, the first message we get will configure the message info
+ if (s_TransportInfo.MessageMemPhysAddr == 0)
+ {
+#if NV_IS_AVP
+ MessageData = NvRmAvpPrivGetUncachedAddress(MessageData);
+#else
+ MessageData = MessageData;
+#endif
+ s_TransportInfo.MessageMemPhysAddr = MessageData;
+ s_TransportInfo.pReceiveMem = (void*)MessageData;
+ s_TransportInfo.pTransmitMem = (void *) (MessageData + MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE);
+ // ack the message and return.
+ *(NvU32*)s_TransportInfo.pReceiveMem = TransportCmd_None;
+ return;
+ }
+
+ // otherwise decode and dispatch the message.
+
+
+ if (s_TransportInfo.pReceiveMem == NULL)
+ {
+ /* QT/EMUTRANS takes this path. */
+ NvRmMemRead(s_TransportInfo.hMessageMem, MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE, s_TmpIsrMsgBuffer, MAX_MESSAGE_LENGTH);
+ pMessage = s_TmpIsrMsgBuffer;
+ NvRmMemWrite(s_TransportInfo.hMessageMem, MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE, s_TmpIsrMsgBuffer, 2*sizeof(NvU32));
+ }
+ else
+ {
+ pMessage = (NvU32*)s_TransportInfo.pReceiveMem;
+ }
+
+ MessageCommand = pMessage[0];
+
+ switch (MessageCommand)
+ {
+ case TransportCmd_Connect:
+ HandleConnectMessage(hDevice, pMessage);
+ break;
+
+ case TransportCmd_Disconnect:
+ HandleDisconnectMessage(hDevice, pMessage);
+ break;
+
+ case TransportCmd_Message:
+ HandlePortMessage(hDevice, pMessage);
+ break;
+
+ default:
+ NV_ASSERT(0);
+ }
+
+ NvRmInterruptDone(s_TransportInterruptHandle);
+}
+
+
+/**
+ * Handle the outbox empty interrupt.
+ */
+static void
+OutboxEmptyIsr(void *args)
+{
+ // !!! This is not currently used... ignore for now. Might be required if we find that we
+ // need to spin for long periods of time waiting for other end of the connection to consume
+ // messages.
+ //
+ NvRmInterruptDone(s_TransportInterruptHandle);
+}
+
+static void
+NvRmPrivProcIdGetProcessorInfo(
+ NvRmDeviceHandle hDevice,
+ NvRmModuleID *pProcModuleId)
+{
+#if NV_IS_AVP
+ *pProcModuleId = NvRmModuleID_Avp;
+#else
+ *pProcModuleId = NvRmModuleID_Cpu;
+#endif
+}
+
+/**
+ * Register for the transport interrupts.
+ */
+static NvError
+RegisterTransportInterrupt(NvRmDeviceHandle hDevice)
+{
+ NvOsInterruptHandler DmaIntHandlers[MAX_INT_FOR_TRANSPORT];
+ NvU32 IrqList[MAX_INT_FOR_TRANSPORT];
+ NvRmModuleID ProcModuleId;
+
+ if (s_TransportInterruptHandle)
+ {
+ return NvSuccess;
+ }
+ NvRmPrivProcIdGetProcessorInfo(hDevice, &ProcModuleId);
+
+ if (ProcModuleId == NvRmModuleID_Cpu)
+ {
+ IrqList[0] = NvRmGetIrqForLogicalInterrupt(
+ hDevice, NvRmModuleID_ResourceSema, CPU_TRANSPORT_INT_IBF);
+ IrqList[1] = NvRmGetIrqForLogicalInterrupt(
+ hDevice, NvRmModuleID_ResourceSema, CPU_TRANSPORT_INT_OBE);
+ }
+ else
+ {
+ IrqList[0] = NvRmGetIrqForLogicalInterrupt(
+ hDevice, NvRmModuleID_ResourceSema, AVP_TRANSPORT_INT_IBF);
+ IrqList[1] = NvRmGetIrqForLogicalInterrupt(
+ hDevice, NvRmModuleID_ResourceSema, AVP_TRANSPORT_INT_OBE);
+ }
+
+ /* There is no need for registering the OutboxEmptyIsr, so we only register
+ * one interrupt i.e. InboxFullIsr */
+ DmaIntHandlers[0] = InboxFullIsr;
+ DmaIntHandlers[1] = OutboxEmptyIsr;
+ return NvRmInterruptRegister(hDevice, 1, IrqList, DmaIntHandlers,
+ hDevice, &s_TransportInterruptHandle, NV_TRUE);
+}
+
+// allocate buffers to be used for sending/receiving messages.
+static void
+NvRmPrivTransportAllocBuffers(NvRmDeviceHandle hRmDevice)
+{
+#if !NV_IS_AVP
+ // These buffers are always allocated on the CPU side. We'll pass the address over the AVP
+ //
+
+ NvError Error = NvSuccess;
+ NvRmMemHandle hNewMemHandle = NULL;
+
+ // Create memory handle
+ Error = NvRmMemHandleCreate(hRmDevice, &hNewMemHandle, (MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE)*2);
+ if (Error)
+ goto fail;
+
+ // Allocates the memory from the Heap
+ Error = NvRmMemAlloc(hNewMemHandle, NULL, 0,
+ XPC_MESSAGE_ALIGNMENT_SIZE, NvOsMemAttribute_Uncached);
+ if (Error)
+ goto fail;
+
+ s_TransportInfo.MessageMemPhysAddr = NvRmMemPin(hNewMemHandle);
+
+ // If it is success to create the memory handle.
+ // We have to be able to get a mapping to this, because it is used at interrupt time!
+ s_TransportInfo.hMessageMem = hNewMemHandle;
+ Error = NvRmMemMap(hNewMemHandle, 0,
+ (MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE)*2,
+ NVOS_MEM_READ_WRITE,
+ &s_TransportInfo.pTransmitMem);
+ if (Error)
+ {
+ s_TransportInfo.pTransmitMem = NULL;
+ s_TransportInfo.pReceiveMem = NULL;
+ }
+ else
+ {
+ s_TransportInfo.pReceiveMem = (void *) (((NvUPtr)s_TransportInfo.pTransmitMem) +
+ MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE);
+ }
+
+ s_TransportInfo.hMessageMem = hNewMemHandle;
+ NvRmMemWr32(hNewMemHandle, 0, 0xdeadf00d); // set this non-zero to throttle messages to the avp till avp is ready.
+ NvRmMemWr32(hNewMemHandle, MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE, 0);
+
+ NvRmPrivXpcSendMessage(s_TransportInfo.hXpc, s_TransportInfo.MessageMemPhysAddr);
+ return;
+
+
+fail:
+ NvRmMemHandleFree(hNewMemHandle);
+ s_TransportInfo.hMessageMem = NULL;
+ return;
+#else
+ return;
+#endif
+}
+
+
+static void
+NvRmPrivTransportFreeBuffers(NvRmDeviceHandle hRmDevice)
+{
+#if !NV_IS_AVP
+ NvRmMemHandleFree(s_TransportInfo.hMessageMem);
+#endif
+}
+
+static volatile NvBool s_Transport_Inited = NV_FALSE;
+
+/**
+ * Initialize the transport structures, this is callled once
+ * at NvRmOpen time.
+ */
+NvError NvRmTransportInit(NvRmDeviceHandle hRmDevice)
+{
+ NvError err;
+
+ NvOsMemset(&s_TransportInfo, 0, sizeof(s_TransportInfo));
+ s_TransportInfo.hDevice = hRmDevice;
+
+ err = NvOsMutexCreate(&s_TransportInfo.mutex);
+ if (err)
+ goto fail;
+
+#if !NVOS_IS_WINDOWS || NVOS_IS_WINDOWS_CE
+ err = NvRmPrivXpcCreate(hRmDevice, &s_TransportInfo.hXpc);
+ if (err)
+ goto fail;
+
+ NvRmPrivTransportAllocBuffers(hRmDevice);
+#endif
+
+ if (1) // Used in EMUTRANS mode where the buffers cannot be mapped.
+ {
+ s_TmpIsrMsgBuffer = NvOsAlloc(MAX_MESSAGE_LENGTH);
+ if (!s_TmpIsrMsgBuffer)
+ goto fail;
+ }
+
+#if LOOPBACK_PROFILE
+ {
+ NvU32 TimerAddr;
+ NvU32 TimerSize;
+
+ NvRmModuleGetBaseAddress(hRmDevice, NvRmModuleID_TimerUs, &TimerAddr, &TimerSize);
+ // map the us counter
+ err = NvRmPhysicalMemMap(TimerAddr, TimerSize, NVOS_MEM_READ_WRITE,
+ NvOsMemAttribute_Uncached, (void*)&s_TransportInfo.pTimer);
+ if (err)
+ goto fail;
+ }
+
+#endif
+
+#if !NVOS_IS_WINDOWS || NVOS_IS_WINDOWS_CE
+ err = RegisterTransportInterrupt(hRmDevice);
+ if (err)
+ goto fail;
+#endif
+ s_Transport_Inited = NV_TRUE;
+ return NvSuccess;
+
+
+fail:
+#if !NVOS_IS_WINDOWS || NVOS_IS_WINDOWS_CE
+ NvRmPrivXpcDestroy(s_TransportInfo.hXpc);
+ NvRmPrivTransportFreeBuffers(hRmDevice);
+#endif
+ NvOsFree(s_TmpIsrMsgBuffer);
+ NvOsMutexDestroy(s_TransportInfo.mutex);
+ return err;
+}
+
+/**
+ * DeInitialize the transport structures.
+ */
+void NvRmTransportDeInit(NvRmDeviceHandle hRmDevice)
+{
+ // Unregister the interrupts.
+#if !NVOS_IS_WINDOWS || NVOS_IS_WINDOWS_CE
+ NvRmPrivXpcDestroy(s_TransportInfo.hXpc);
+ NvRmPrivTransportFreeBuffers(hRmDevice);
+ NvRmInterruptUnregister(hRmDevice, s_TransportInterruptHandle);
+ s_TransportInterruptHandle = NULL;
+#endif
+ NvOsFree(s_TmpIsrMsgBuffer);
+ NvOsMutexDestroy(s_TransportInfo.mutex);
+}
+
+
+static void
+InsertPort(NvRmDeviceHandle hDevice, NvRmTransportHandle hPort)
+{
+ hPort->pNext = s_TransportInfo.pPortHead;
+ s_TransportInfo.pPortHead = hPort;
+}
+
+
+static NvRmTransportHandle
+FindPort(NvRmDeviceHandle hDevice, char *pPortName)
+{
+ NvRmTransportHandle hPort = NULL;
+ NvRmTransportHandle hIter = NULL;
+
+ hIter = s_TransportInfo.pPortHead;
+ while (hIter)
+ {
+ if ( NvOsStrcmp(pPortName, hIter->PortName) == 0)
+ {
+ hPort = hIter;
+ break;
+ }
+ hIter = hIter->pNext;
+ }
+
+ return hPort;
+}
+
+
+// Remove the given hPort from the list of ports
+static void
+DeletePort(NvRmDeviceHandle hRmDevice, const NvRmTransportHandle hPort)
+{
+ // Pointer to the pointer alleviates all special cases in linked list walking.
+ // I wish I was clever enough to have figured this out myself.
+
+ NvRmTransportHandle *hIter;
+
+ hIter = &s_TransportInfo.pPortHead;
+ while (*hIter)
+ {
+ if ( *hIter == hPort )
+ {
+ *hIter = (*hIter)->pNext;
+ break;
+ }
+ hIter = &(*hIter)->pNext;
+ }
+}
+
+
+
+
+/**
+ * Open the port handle with a given port name. With the same name, only two
+ * port can be open.
+ * Thread Safety: It is done inside the function.
+ */
+
+NvError
+NvRmTransportOpen(
+ NvRmDeviceHandle hRmDevice,
+ char *pPortName,
+ NvOsSemaphoreHandle RecvMessageSemaphore,
+ NvRmTransportHandle *phTransport)
+{
+ NvU32 PortNameLen;
+ NvRmTransportHandle hPartner = NULL;
+ NvRmTransportHandle hPort = NULL;
+ NvError err = NvError_InsufficientMemory;
+ char TmpName[MAX_PORT_NAME_LENGTH+1];
+
+ while (!s_Transport_Inited) {
+ // This can happen, if this API is called before avp init.
+ NvOsSleepMS(500);
+ }
+ // Look and see if this port exists anywhere.
+ if (pPortName == NULL)
+ {
+ NvOsMutexLock(s_TransportInfo.mutex);
+
+ pPortName = NvRmPrivTransportUniqueName();
+ PortNameLen = NvOsStrlen(pPortName);
+ NvOsStrncpy(TmpName, pPortName, sizeof(TmpName) );
+ pPortName = TmpName;
+
+ NvOsMutexUnlock(s_TransportInfo.mutex);
+ }
+ else
+ {
+ PortNameLen = NvOsStrlen(pPortName);
+ NV_ASSERT(PortNameLen <= MAX_PORT_NAME_LENGTH);
+ }
+
+ NvOsMutexLock(s_TransportInfo.mutex);
+ hPartner = FindPort(hRmDevice, pPortName);
+
+ if (hPartner && hPartner->hConnectedPort != NULL)
+ {
+ NvOsMutexUnlock(s_TransportInfo.mutex);
+ return NvError_TransportPortAlreadyExist;
+ }
+
+ // check if this is one of the special RPC ports used by the rm
+ if ( NvOsStrcmp(pPortName, "RPC_AVP_PORT") == 0)
+ {
+ //If someone else wants to open this port
+ //just return the one already created.
+ if (hPartner)
+ {
+ hPort = hPartner;
+ goto success;
+ }
+ else
+ {
+ hPort = &s_RpcAvpPortStruct;
+ hPort->RecvMessageQueue.pReceiveMsg = (void *)&s_RpcAvpQueue[0];
+ }
+ }
+ else if (NvOsStrcmp(pPortName, "RPC_CPU_PORT") == 0)
+ {
+ hPort = &s_RpcCpuPortStruct;
+ hPort->RecvMessageQueue.pReceiveMsg = (void *)&s_RpcCpuQueue[0];
+ }
+ else
+ {
+ // Create a new TransportPort
+ hPort = NvOsAlloc( sizeof(*hPort) );
+ if (!hPort)
+ goto fail;
+
+ NvOsMemset(hPort, 0, sizeof(*hPort) );
+
+ // Allocate the receive queue
+ hPort->RecvMessageQueue.pReceiveMsg = NvOsAlloc( sizeof(RmReceiveMessage) * (MAX_MESSAGE_DEPTH+1));
+ if (!hPort->RecvMessageQueue.pReceiveMsg)
+ goto fail;
+ }
+
+ NvOsStrncpy(hPort->PortName, pPortName, PortNameLen);
+ hPort->State = PortState_Open;
+ hPort->hConnectedPort = hPartner;
+
+ if (RecvMessageSemaphore)
+ {
+ err = NvOsSemaphoreClone(RecvMessageSemaphore, &hPort->hOnPushMsgSem);
+ if (err)
+ goto fail;
+ }
+
+ hPort->RecvMessageQueue.QueueSize = MAX_MESSAGE_DEPTH+1;
+ hPort->hRmDevice = hRmDevice;
+
+ if (hPort->hConnectedPort != NULL)
+ {
+ hPort->hConnectedPort->hConnectedPort = hPort;
+ }
+ InsertPort(hRmDevice, hPort);
+
+
+ // !!! loopback info
+#if LOOPBACK_PROFILE
+ if (NvOsStrcmp(hPort->PortName, "LOOPTEST") == 0)
+ hPort->bLoopTest = 1;
+#endif
+
+success:
+ NvOsMutexUnlock(s_TransportInfo.mutex);
+ *phTransport = hPort;
+ return NvSuccess;
+
+fail:
+ if (hPort)
+ {
+ NvOsFree(hPort->RecvMessageQueue.pReceiveMsg);
+ NvOsSemaphoreDestroy(hPort->hOnPushMsgSem);
+ NvOsFree(hPort);
+ hPort = NULL;
+ }
+ NvOsMutexUnlock(s_TransportInfo.mutex);
+
+ return err;
+}
+
+
+/**
+ * Close the transport handle
+ * Thread Safety: It is done inside the function.
+ */
+void NvRmTransportClose(NvRmTransportHandle hPort)
+{
+ NvU32 RemoteMessage[4];
+
+ if (!hPort)
+ return;
+
+ // Look and see if this port exists anywhere.
+ NV_ASSERT(hPort);
+
+
+ NvOsMutexLock(s_TransportInfo.mutex);
+ DeletePort(hPort->hRmDevice, hPort); // unlink this port
+
+ // Check if there is already a port waiting to connect, and if there is
+ // switch the port state to _Destroy, and signal the waiters semaphore.
+ // The "State" member is not protected by the mutex because it can be
+ // updated by the ISR.
+ while (hPort->State == PortState_Waiting)
+ {
+ NvOsAtomicCompareExchange32((NvS32*)&hPort->State, PortState_Waiting, PortState_Destroy);
+ if (hPort->State == PortState_Destroy)
+ {
+ NvOsSemaphoreSignal(hPort->hOnConnectSem);
+
+ // in this case, we can't complete the destroy, the signalled thread will
+ // have to complete. We just return now
+ NvOsMutexUnlock(s_TransportInfo.mutex);
+ return;
+ }
+ }
+
+ if (hPort->hConnectedPort)
+ {
+ // unlink this port from the other side of the connection.
+ hPort->hConnectedPort->hConnectedPort = NULL;
+ }
+
+ if (hPort->RemotePort)
+ {
+ RemoteMessage[0] = TransportCmd_Disconnect;
+ RemoteMessage[1] = hPort->RemotePort;
+ NvRmPrivTransportSendMessage(hPort->hRmDevice, RemoteMessage, 2*sizeof(NvU32));
+ }
+
+ NvOsSemaphoreDestroy(hPort->hOnPushMsgSem);
+
+
+ if (hPort == &s_RpcAvpPortStruct ||
+ hPort == &s_RpcCpuPortStruct)
+ {
+ // don't free these..
+ NvOsMemset(hPort, 0, sizeof(*hPort));
+ }
+ else
+ {
+ NvOsFree(hPort->RecvMessageQueue.pReceiveMsg);
+ NvOsFree(hPort);
+ }
+
+ NvOsMutexUnlock(s_TransportInfo.mutex);
+}
+
+
+/**
+ * Wait for the connection to the other end.
+ * Thread Safety: It is done inside the function.
+ */
+NvError
+NvRmTransportWaitForConnect(
+ NvRmTransportHandle hPort,
+ NvU32 TimeoutMS)
+{
+ NvOsSemaphoreHandle hSem = NULL;
+ NvError err = NvSuccess;
+
+ NvOsMutexLock(s_TransportInfo.mutex);
+ if (hPort->State != PortState_Open)
+ {
+ NvOsMutexUnlock(s_TransportInfo.mutex);
+ err = NvError_TransportPortAlreadyExist;
+ goto exit_gracefully;
+ }
+
+ err = NvOsSemaphoreCreate(&hSem, 0);
+ if (err)
+ {
+ NvOsMutexUnlock(s_TransportInfo.mutex);
+ goto exit_gracefully;
+ }
+
+ hPort->hOnConnectSem = hSem;
+ hPort->State = PortState_Waiting;
+ NvOsMutexUnlock(s_TransportInfo.mutex);
+
+ err = NvOsSemaphoreWaitTimeout(hSem, TimeoutMS);
+ if (err)
+ {
+ // we have to be careful here, the ISR _might_ happen just after the semaphore
+ // times out.
+ NvOsAtomicCompareExchange32((NvS32 *)&hPort->State, PortState_Waiting, PortState_Open);
+ NV_ASSERT(hPort->State == PortState_Open || hPort->State == PortState_Connected);
+ if (hPort->State == PortState_Connected)
+ {
+ err = NvSuccess;
+ }
+ }
+
+ NvOsMutexLock(s_TransportInfo.mutex);
+ hPort->hOnConnectSem = NULL;
+ NvOsMutexUnlock(s_TransportInfo.mutex);
+
+ if (hPort->State == PortState_Destroy)
+ {
+ // finish the destroy process
+ NvRmTransportClose(hPort);
+ err = NvError_TransportConnectionFailed;
+ }
+
+exit_gracefully:
+ NvOsSemaphoreDestroy(hSem);
+ return err;
+}
+
+
+
+static NvError
+NvRmPrivTransportWaitResponse(NvRmDeviceHandle hDevice, NvU32 *response, NvU32 ResponseLength, NvU32 TimeoutMS)
+{
+ NvU32 CurrentTime;
+ NvU32 StartTime;
+ NvU32 Response;
+ NvBool GotResponse = NV_TRUE;
+ NvError err = NvError_Timeout;
+ volatile NvU32 *pXpcMessage = (volatile NvU32*)s_TransportInfo.pTransmitMem;
+
+ if (pXpcMessage == NULL)
+ {
+ if (!NV_IS_AVP)
+ {
+ Response = NvRmMemRd32(s_TransportInfo.hMessageMem, 0);
+ } else
+ {
+ NV_ASSERT(0);
+ return NvSuccess;
+ }
+ }
+ else
+ {
+ Response = pXpcMessage[0];
+ }
+
+ if (Response != TransportCmd_Response)
+ {
+ GotResponse = NV_FALSE;
+
+ // response is not back yet, so spin till its here.
+ StartTime = NvOsGetTimeMS();
+ CurrentTime = StartTime;
+ while ( (CurrentTime - StartTime) < TimeoutMS )
+ {
+ if ( pXpcMessage && (pXpcMessage[0] == TransportCmd_Response) )
+ {
+ GotResponse = NV_TRUE;
+ break;
+ }
+ else if ( !pXpcMessage )
+ {
+ NV_ASSERT(!"Invalid pXpcMessage pointer is accessed");
+ }
+ CurrentTime = NvOsGetTimeMS();
+ }
+ }
+
+ if ( pXpcMessage && GotResponse )
+ {
+ err = NvSuccess;
+ NvOsMemcpy(response, (void *)pXpcMessage, ResponseLength);
+ }
+
+ return err;
+}
+
+
+static NvError
+NvRmPrivTransportSendMessage(NvRmDeviceHandle hDevice, NvU32 *message, NvU32 MessageLength)
+{
+ NvU32 ReadData;
+
+ if (s_TransportInfo.pTransmitMem == NULL)
+ {
+ /* QT/EMUTRANS takes this code path */
+ if (!NV_IS_AVP)
+ {
+ ReadData = NvRmMemRd32(s_TransportInfo.hMessageMem, 0);
+ } else
+ {
+ NV_ASSERT(0);
+ return NvSuccess;
+ }
+ }
+ else
+ {
+ ReadData = ((NvU32*)s_TransportInfo.pTransmitMem)[0];
+ }
+
+ // Check for clear to send
+ if ( ReadData != 0)
+ return NvError_TransportMessageBoxFull; // someone else is sending a message
+
+ if (s_TransportInfo.pTransmitMem == NULL)
+ {
+ /* QT/EMUTRANS takes this code path */
+ NvRmMemWrite(s_TransportInfo.hMessageMem, 0, message, MessageLength);
+ }
+ else
+ {
+ NvOsMemcpy(s_TransportInfo.pTransmitMem, message, MessageLength);
+ NvOsFlushWriteCombineBuffer();
+ }
+ NvRmPrivXpcSendMessage(s_TransportInfo.hXpc, s_TransportInfo.MessageMemPhysAddr);
+ return NvSuccess;
+}
+
+NvError
+NvRmTransportSendMsgInLP0(NvRmTransportHandle hPort, void *pMessageBuffer, NvU32 MessageSize)
+{
+ NvU32 ReadData;
+ NvU32 Message[3 + ((MAX_MESSAGE_LENGTH) / sizeof(NvU32))];
+
+ NV_ASSERT(pMessageBuffer);
+
+ Message[0] = TransportCmd_Message;
+ Message[1] = hPort->RemotePort;
+ Message[2] = MessageSize;
+
+ if (MessageSize)
+ NvOsMemcpy(&Message[3], pMessageBuffer, MessageSize);
+
+ ReadData = ((NvU32*)s_TransportInfo.pTransmitMem)[0];
+
+ // Check for clear to send
+ if ( ReadData != 0)
+ return NvError_TransportMessageBoxFull; // someone else is sending a message
+
+ NvOsMemcpy(s_TransportInfo.pTransmitMem, Message, MessageSize + 3*sizeof(NvU32));
+ NvOsFlushWriteCombineBuffer();
+
+ NvRmPrivXpcSendMessage(s_TransportInfo.hXpc, s_TransportInfo.MessageMemPhysAddr);
+ return NvSuccess;
+}
+
+static void
+NvRmPrivTransportClearSend(NvRmDeviceHandle hDevice)
+{
+ if (s_TransportInfo.pTransmitMem == NULL)
+ {
+ /* QT/EMUTRANS take this path */
+ if (!NV_IS_AVP)
+ {
+ NvRmMemWr32(s_TransportInfo.hMessageMem, 0, TransportCmd_None);
+ } else
+ {
+ NV_ASSERT(0);
+ }
+ }
+ else
+ {
+ ((NvU32*)s_TransportInfo.pTransmitMem)[0] = TransportCmd_None;
+ }
+}
+
+/**
+ * Make the connection to the other end.
+ * Thread Safety: It is done inside the function.
+ */
+NvError NvRmTransportConnect(NvRmTransportHandle hPort, NvU32 TimeoutMS)
+{
+ NvRmTransportHandle hPartnerPort;
+ NvU32 StartTime;
+ NvU32 CurrentTime;
+ NvU32 ConnectMessage[ MAX_PORT_NAME_LENGTH/4 + 3];
+ NvError err;
+
+
+ // Look and see if there is a local port with the same name that is currently waiting, if there is
+ // mark both ports as connected.
+
+ NV_ASSERT(hPort);
+ NV_ASSERT(hPort->hRmDevice);
+ NV_ASSERT(hPort->State == PortState_Open);
+
+
+ StartTime = NvOsGetTimeMS();
+ for (;;)
+ {
+ // Someone is waiting for a connection here locally.
+ NvOsMutexLock(s_TransportInfo.mutex);
+
+ hPartnerPort = hPort->hConnectedPort;
+ if (hPartnerPort)
+ {
+ // Found a local connection
+ if (hPartnerPort->State == PortState_Waiting)
+ {
+
+ hPartnerPort->State = PortState_Connected;
+ hPartnerPort->hConnectedPort = hPort;
+
+ hPort->State = PortState_Connected;
+ NvOsSemaphoreSignal(hPartnerPort->hOnConnectSem);
+ break;
+ }
+ }
+ else if (s_TransportInfo.hMessageMem || s_TransportInfo.pReceiveMem) // if no shared buffer, then we can't create a remote connection.
+ {
+ ConnectMessage[0] = TransportCmd_Connect;
+ ConnectMessage[1] = (NvU32)hPort;
+ NvOsMemcpy(&ConnectMessage[2], hPort->PortName, MAX_PORT_NAME_LENGTH);
+
+ err = NvRmPrivTransportSendMessage(hPort->hRmDevice, ConnectMessage, sizeof(ConnectMessage));
+ if (!err)
+ {
+ // should send back 2 words of data. Give remote side 1000ms to respond, which should be about 100x more
+ // than it needs.
+ NvU32 WaitTime = NV_MAX(1000, TimeoutMS);
+ if (TimeoutMS == NV_WAIT_INFINITE)
+ TimeoutMS = NV_WAIT_INFINITE;
+
+ // !!! Note, we can do this without holding the mutex...
+ err = NvRmPrivTransportWaitResponse(hPort->hRmDevice, ConnectMessage, 2*sizeof(NvU32), WaitTime);
+ NvRmPrivTransportClearSend(hPort->hRmDevice);
+ if (err)
+ {
+ // the other side is not responding to messages, doh!
+ NvOsMutexUnlock(s_TransportInfo.mutex);
+ return NvError_TransportConnectionFailed;
+ }
+
+ // check the response
+ hPort->RemotePort = ConnectMessage[1];
+ if (hPort->RemotePort != 0)
+ {
+ hPort->State = PortState_Connected;
+ break;
+ }
+ }
+ }
+ NvOsMutexUnlock(s_TransportInfo.mutex);
+ NV_ASSERT(hPort->State == PortState_Open); // it better still be open
+
+ // Didn't find a connection, wait a few ms and then try again
+ CurrentTime = NvOsGetTimeMS();
+ if ( (CurrentTime - StartTime) > TimeoutMS )
+ return NvError_Timeout;
+
+ NvOsSleepMS(10);
+ }
+
+ NvOsMutexUnlock(s_TransportInfo.mutex);
+ return NvSuccess;
+}
+
+
+/**
+ * Set the queue depth and message size of the transport handle.
+ * Thread Safety: It is done inside the function.
+ */
+NvError NvRmTransportSetQueueDepth(
+ NvRmTransportHandle hPort,
+ NvU32 MaxQueueDepth,
+ NvU32 MaxMessageSize)
+{
+ RmReceiveMessage *pNewReceiveMsg = NULL;
+
+ NV_ASSERT(hPort != NULL);
+ NV_ASSERT(MaxQueueDepth != 0);
+ NV_ASSERT(MaxMessageSize != 0);
+
+ // You cannot change the queue after a connection has been opened
+ NV_ASSERT(hPort->State == PortState_Open);
+
+ // !!! FIXME
+ // Xpc does not allow changing the base message size, so we can't change the message size here (yet!)
+ // Once we have per port message buffers we can set this.
+ NV_ASSERT(MaxMessageSize <= MAX_MESSAGE_LENGTH);
+
+ // These are statically allocated ports, they cannot be modified!
+ // !!! FIXME: this is just a sanity check. Remove this and make it so that
+ // cpu/avp rpc doesn't call this function and just knows that the
+ // transport will give it a port with a large enough queue to support
+ // rpc, since rpc ports and queue are statically allocated this has to be true.
+ if (hPort == &s_RpcAvpPortStruct ||
+ hPort == &s_RpcCpuPortStruct)
+ {
+ if (MaxMessageSize <= MAX_MESSAGE_LENGTH &&
+ MaxQueueDepth <= MAX_MESSAGE_DEPTH)
+ {
+ return NvSuccess;
+ }
+
+ NV_ASSERT(!" Illegal meesage length or queue depth. ");
+ }
+
+ // Freeing default allocated message queue.
+ NvOsFree(hPort->RecvMessageQueue.pReceiveMsg);
+ hPort->RecvMessageQueue.pReceiveMsg = NULL;
+ // create a new message queue struct, one longer than requested on purpose.
+ pNewReceiveMsg = NvOsAlloc( sizeof(RmReceiveMessage) * (MaxQueueDepth+1));
+ if (pNewReceiveMsg == NULL)
+ return NvError_InsufficientMemory;
+
+ hPort->RecvMessageQueue.pReceiveMsg = pNewReceiveMsg;
+ hPort->RecvMessageQueue.QueueSize = (NvU16)(MaxQueueDepth+1);
+
+ return NvSuccess;
+}
+
+
+static NvError
+NvRmPrivTransportSendRemoteMsg(
+ NvRmTransportHandle hPort,
+ void* pMessageBuffer,
+ NvU32 MessageSize,
+ NvU32 TimeoutMS)
+{
+ NvError err;
+ NvU32 StartTime;
+ NvU32 CurrentTime;
+ NvU32 Message[3 + ((MAX_MESSAGE_LENGTH) / sizeof(NvU32))];
+
+ NV_ASSERT((MAX_MESSAGE_LENGTH) >= MessageSize);
+
+ StartTime = NvOsGetTimeMS();
+
+ Message[0] = TransportCmd_Message;
+ Message[1] = hPort->RemotePort;
+ Message[2] = MessageSize;
+
+ NvOsMemcpy(&Message[3], pMessageBuffer, MessageSize);
+
+ for (;;)
+ {
+ NvOsMutexLock(s_TransportInfo.mutex);
+ err = NvRmPrivTransportSendMessage(hPort->hRmDevice, Message, MessageSize + 3*sizeof(NvU32));
+ NvOsMutexUnlock(s_TransportInfo.mutex);
+ if (err == NvSuccess)
+ {
+ return NvSuccess;
+ }
+
+ // Sleep and then try again in a few ms to send again
+ CurrentTime = NvOsGetTimeMS();
+ if ( TimeoutMS != NV_WAIT_INFINITE && (CurrentTime - StartTime) > TimeoutMS )
+ return NvError_Timeout;
+
+ NvOsSleepMS(1); // try again later...
+ }
+}
+
+
+
+static NvError
+NvRmPrivTransportSendLocalMsg(
+ NvRmTransportHandle hPort,
+ void* pMessageBuffer,
+ NvU32 MessageSize,
+ NvU32 TimeoutMS)
+{
+ NvU32 CurrentTime;
+ NvU32 StartTime;
+ NvError err = NvSuccess;
+
+ NvRmTransportHandle hRemotePort;
+
+ NvOsMutexLock(s_TransportInfo.mutex);
+ hRemotePort = hPort->hConnectedPort;
+
+
+ StartTime = NvOsGetTimeMS();
+
+ for (;;)
+ {
+ // try to insert into the message into the receivers queue.
+ NvBool bSuccess = InsertMessage(hRemotePort, (NvU8*)pMessageBuffer, MessageSize);
+ if (bSuccess)
+ {
+ if (hRemotePort->hOnPushMsgSem)
+ NvOsSemaphoreSignal(hRemotePort->hOnPushMsgSem);
+ break;
+ }
+
+ // The destination port is full.
+ if (TimeoutMS == 0)
+ {
+ err = NvError_TransportMessageBoxFull;
+ break;
+ }
+
+ // The user wants a timeout, so we just sleep a short time so the
+ // other thread can pop a message. It would be better to use another semaphore
+ // to indicate that the box is not full, but that just seems overkill since this
+ // should rarely happen anyhow.
+ // unlock the mutex, and wait a small amount of time.
+ NvOsMutexUnlock(s_TransportInfo.mutex);
+
+ NvOsSleepMS(1);
+ NvOsMutexLock(s_TransportInfo.mutex);
+ if (TimeoutMS != NV_WAIT_INFINITE)
+ {
+ // check for a timeout condition.
+ CurrentTime = NvOsGetTimeMS();
+ if ( (CurrentTime - StartTime) >= TimeoutMS)
+ {
+ err = NvError_Timeout;
+ break;
+ }
+ }
+ }
+ NvOsMutexUnlock(s_TransportInfo.mutex);
+
+ return err;
+}
+
+
+/**
+ * Send the message to the other end port.
+ * Thread Safety: It is done inside the function.
+ */
+NvError
+NvRmTransportSendMsg(
+ NvRmTransportHandle hPort,
+ void* pMessageBuffer,
+ NvU32 MessageSize,
+ NvU32 TimeoutMS)
+{
+ NvError err;
+
+ NV_ASSERT(hPort);
+ NV_ASSERT(hPort->State == PortState_Connected);
+ NV_ASSERT(pMessageBuffer);
+
+#if LOOPBACK_PROFILE
+ if (hPort->bLoopTest)
+ {
+# if NV_IS_AVP
+ ((NvU32*)pMessageBuffer)[LOOP_AVP_SEND_INDEX] = *s_TransportInfo.pTimer;
+# else
+ ((NvU32*)pMessageBuffer)[LOOP_CPU_SEND_INDEX] = *s_TransportInfo.pTimer;
+# endif
+ }
+#endif
+
+ if (hPort->hConnectedPort)
+ {
+ err = NvRmPrivTransportSendLocalMsg(hPort, pMessageBuffer, MessageSize, TimeoutMS);
+ }
+ else if (hPort->State == PortState_Connected)
+ {
+ err = NvRmPrivTransportSendRemoteMsg(hPort, pMessageBuffer, MessageSize, TimeoutMS);
+ }
+ else
+ {
+ NV_ASSERT(0); // someone did something naughty
+ err = NvError_TransportNotConnected;
+ }
+
+ return err;
+}
+
+
+
+/**
+ * Receive the message from the other end port.
+ * Thread Safety: It is done inside the function.
+ */
+NvError
+NvRmTransportRecvMsg(
+ NvRmTransportHandle hPort,
+ void* pMessageBuffer,
+ NvU32 MaxSize,
+ NvU32 *pMessageSize)
+{
+ NvU8 TmpMessage[MAX_MESSAGE_LENGTH];
+
+ NV_ASSERT(hPort);
+ NV_ASSERT( (hPort->State == PortState_Connected) || (hPort->State == PortState_Disconnected) );
+ NV_ASSERT(pMessageBuffer);
+ NV_ASSERT(pMessageSize);
+
+
+ NvOsMutexLock(s_TransportInfo.mutex);
+ if (hPort->RecvMessageQueue.ReadIndex == hPort->RecvMessageQueue.WriteIndex)
+ {
+ NvOsMutexUnlock(s_TransportInfo.mutex);
+ return NvError_TransportMessageBoxEmpty;
+ }
+
+ ExtractMessage(hPort, (NvU8*)pMessageBuffer, pMessageSize, MaxSize);
+ if (*pMessageSize > MaxSize)
+ {
+ // not enough room to copy the message
+ NvOsMutexUnlock(s_TransportInfo.mutex);
+ NV_ASSERT(!" RM Transport: Illegal message size. ");
+ }
+
+
+ // if there was backpressure asserted, try to handle the currently posted message, and re-enable messages
+ if (s_TransportInfo.ReceiveBackPressureOn != s_TransportInfo.ReceiveBackPressureOff)
+ {
+ NV_ASSERT( ((NvU8)s_TransportInfo.ReceiveBackPressureOn) == ((NvU8)(s_TransportInfo.ReceiveBackPressureOff+1)) );
+ ++s_TransportInfo.ReceiveBackPressureOff;
+
+ if (s_TransportInfo.pReceiveMem == NULL)
+ {
+ /* QT/EMUTRANS takes this path. */
+ NvRmMemRead(s_TransportInfo.hMessageMem,
+ MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE,
+ TmpMessage,
+ MAX_MESSAGE_LENGTH);
+ HandlePortMessage(hPort->hRmDevice, (volatile void *)TmpMessage);
+ NvRmMemWrite(s_TransportInfo.hMessageMem,
+ MAX_MESSAGE_LENGTH + MAX_COMMAND_SIZE,
+ TmpMessage,
+ 2*sizeof(NvU32) );
+ }
+ else
+ {
+ HandlePortMessage(hPort->hRmDevice, (NvU32*)s_TransportInfo.pReceiveMem);
+ }
+ }
+
+#if LOOPBACK_PROFILE
+ if (hPort->bLoopTest)
+ {
+# if NV_IS_AVP
+ ((NvU32*)pMessageBuffer)[LOOP_AVP_RECV_INDEX] = *s_TransportInfo.pTimer;
+# else
+ ((NvU32*)pMessageBuffer)[LOOP_CPU_RECV_INDEX] = *s_TransportInfo.pTimer;
+# endif
+ }
+#endif
+
+ NvOsMutexUnlock(s_TransportInfo.mutex);
+
+ return NvSuccess;
+}
+
+void
+NvRmTransportGetPortName(
+ NvRmTransportHandle hPort,
+ NvU8 *PortName,
+ NvU32 PortNameSize )
+{
+ NvU32 len;
+
+ NV_ASSERT(hPort);
+ NV_ASSERT(PortName);
+
+ len = NvOsStrlen(hPort->PortName);
+ if (len >= PortNameSize)
+ {
+ NV_ASSERT(!" RM Transport: Port Name too long. ");
+ }
+
+ NvOsStrncpy((char *)PortName, hPort->PortName, PortNameSize);
+}
+