mirror of
https://github.com/NVIDIA/open-gpu-kernel-modules.git
synced 2026-02-02 06:29:47 +00:00
626 lines
18 KiB
C++
626 lines
18 KiB
C++
/*
|
|
* SPDX-FileCopyrightText: Copyright (c) 2010-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
/******************************* DisplayPort *******************************\
|
|
* *
|
|
* Module: dp_messages.cpp *
|
|
* Encoding for aux common messages. *
|
|
* *
|
|
\***************************************************************************/
|
|
#include "dp_internal.h"
|
|
#include "dp_bitstream.h"
|
|
#include "dp_splitter.h"
|
|
#include "dp_messages.h"
|
|
#include "dp_merger.h"
|
|
#include "dp_list.h"
|
|
#include "dp_tracing.h"
|
|
|
|
using namespace DisplayPort;
|
|
namespace DisplayPort
|
|
{
|
|
GenericMessageCompletion::GenericMessageCompletion() :
|
|
failed(false), completed(false) {}
|
|
void GenericMessageCompletion::messageFailed(MessageManager::Message * from, NakData * data)
|
|
{
|
|
nakData = *data;
|
|
failed = true;
|
|
completed = true;
|
|
}
|
|
void GenericMessageCompletion::messageCompleted(MessageManager::Message * from)
|
|
{
|
|
failed = false;
|
|
completed = true;
|
|
}
|
|
};
|
|
|
|
//
|
|
// Transmit a message and wait for the response in place.
|
|
//
|
|
bool MessageManager::send(MessageManager::Message * message, NakData & nakData)
|
|
{
|
|
GenericMessageCompletion completion;
|
|
Address::StringBuffer sb;
|
|
DP_USED(sb);
|
|
|
|
NvU64 startTime, elapsedTime;
|
|
|
|
if (bNoReplyTimerForBusyWaiting)
|
|
{
|
|
message->bBusyWaiting = true;
|
|
}
|
|
post(message, &completion);
|
|
startTime = timer->getTimeUs();
|
|
do
|
|
{
|
|
if (bDpcdProbingForBusyWaiting)
|
|
{
|
|
hal->updateDPCDOffline();
|
|
if (hal->isDpcdOffline())
|
|
{
|
|
DP_LOG(("DP-MM> Device went offline while waiting for reply and so ignoring message %p (ID = %02X, target = %s)",
|
|
(Message*)this, ((Message*)this)->requestIdentifier, (((Message*)this)->state.target).toString(sb)));
|
|
|
|
nakData = completion.nakData;
|
|
completion.failed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
hal->notifyIRQ();
|
|
if (hal->interruptDownReplyReady())
|
|
IRQDownReply();
|
|
|
|
if (completion.completed)
|
|
{
|
|
nakData = completion.nakData;
|
|
break;
|
|
}
|
|
|
|
elapsedTime = timer->getTimeUs() - startTime;
|
|
|
|
if (elapsedTime > (DPCD_MESSAGE_REPLY_TIMEOUT * 1000))
|
|
{
|
|
message->expired(NULL);
|
|
nakData.reason = NakTimeout;
|
|
break;
|
|
}
|
|
|
|
// Sleep while processing timer callbacks
|
|
timer->sleep(1);
|
|
} while(true);
|
|
|
|
return !completion.failed;
|
|
}
|
|
|
|
bool DisplayPort::extractGUID(BitStreamReader * reader, GUID * guid)
|
|
{
|
|
for (unsigned i=0; i < 128; i += 8)
|
|
{
|
|
unsigned data;
|
|
if (!reader->read(&data, 8))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
guid->data[i/8] = (NvU8)data;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void MessageManager::messagedReceived(IncomingTransactionManager * from, EncodedMessage * message)
|
|
{
|
|
if (from == &mergerUpRequest)
|
|
{
|
|
onUpRequestReceived(true, message);
|
|
}
|
|
else
|
|
{
|
|
onDownReplyReceived(true, message);
|
|
}
|
|
}
|
|
|
|
void MessageManager::Message::splitterFailed(OutgoingTransactionManager * from)
|
|
{
|
|
//
|
|
// Message failed
|
|
//
|
|
NakData nakData;
|
|
nakData.reason = NakTimeout;
|
|
MessageManager * parent = this->parent;
|
|
|
|
if (sink)
|
|
sink->messageFailed(this, &nakData);
|
|
|
|
if (from == &parent->splitterDownRequest)
|
|
{
|
|
//
|
|
// Tell the message manager he may begin sending the next message
|
|
//
|
|
parent->transmitAwaitingDownRequests();
|
|
}
|
|
else
|
|
{
|
|
parent->transmitAwaitingUpReplies();
|
|
}
|
|
}
|
|
|
|
void MessageManager::Message::splitterTransmitted(OutgoingTransactionManager * from)
|
|
{
|
|
bTransmitted = true;
|
|
MessageManager * parent = this->parent;
|
|
|
|
if (from == &parent->splitterDownRequest)
|
|
{
|
|
// Client will busy-waiting for the message to complete, we don't need the countdown timer.
|
|
if (!bBusyWaiting)
|
|
{
|
|
// Start the countdown timer for the reply
|
|
parent->timer->queueCallback(this, "SPLI", DPCD_MESSAGE_REPLY_TIMEOUT);
|
|
}
|
|
// Tell the message manager he may begin sending the next message
|
|
parent->transmitAwaitingDownRequests();
|
|
}
|
|
else // UpReply
|
|
{
|
|
if (sink)
|
|
sink->messageCompleted(this); // This is the end for an up reply
|
|
|
|
parent->transmitAwaitingUpReplies();
|
|
}
|
|
|
|
}
|
|
|
|
// Since transmit DPCD_MESSAGE_REPLY_TIMEOUT time has elapsed.
|
|
// - Let's assume the message was not replied to
|
|
void MessageManager::Message::expired(const void * tag)
|
|
{
|
|
Address::StringBuffer sb;
|
|
DP_USED(sb);
|
|
|
|
DP_LOG(("DP-MM> Message transmit time expired on message %p (ID = %02X, target = %s)",
|
|
(Message*)this, ((Message*)this)->requestIdentifier, (((Message*)this)->state.target).toString(sb)));
|
|
|
|
Address::NvU32Buffer addrBuffer;
|
|
dpMemZero(addrBuffer, sizeof(addrBuffer));
|
|
(((Message*)this)->state.target).toNvU32Buffer(addrBuffer);
|
|
NV_DPTRACE_WARNING(MESSAGE_EXPIRED, ((Message*)this)->requestIdentifier, (((Message*)this)->state.target).size(),
|
|
addrBuffer[0], addrBuffer[1], addrBuffer[2], addrBuffer[3]);
|
|
|
|
NakData nakData;
|
|
nakData.reason = NakTimeout;
|
|
|
|
MessageManager * parent = this->parent;
|
|
|
|
DP_ASSERT(parent);
|
|
if (parent && !parent->isBeingDestroyed)
|
|
{
|
|
parent->awaitingReplyDownRequest.remove(this);
|
|
parent->clearPendingMsg();
|
|
parent->transmitAwaitingDownRequests();
|
|
parent->transmitAwaitingUpReplies();
|
|
}
|
|
|
|
if (sink)
|
|
sink->messageFailed(this, &nakData);
|
|
}
|
|
|
|
//
|
|
// Enqueue the next message to the splitterDownRequest
|
|
//
|
|
void MessageManager::transmitAwaitingDownRequests()
|
|
{
|
|
for (ListElement * i = notYetSentDownRequest.begin(); i!=notYetSentDownRequest.end(); )
|
|
{
|
|
Message * m = (Message *)i;
|
|
i = i->next; // Do this first since we may unlink the current node
|
|
|
|
if (awaitingReplyDownRequest.isEmpty())
|
|
{
|
|
//
|
|
// Set the message number, and unlink from the outgoing queue
|
|
//
|
|
m->encodedMessage.messageNumber = 0;
|
|
m->state.messageNumber = 0;
|
|
|
|
notYetSentDownRequest.remove(m);
|
|
awaitingReplyDownRequest.insertBack(m);
|
|
|
|
//
|
|
// This call can cause transmitAwaitingDownRequests to be called again
|
|
//
|
|
bool sent = splitterDownRequest.send(m->encodedMessage, m);
|
|
DP_ASSERT(sent);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Enqueue the next message to the splitterUpReply
|
|
//
|
|
void MessageManager::transmitAwaitingUpReplies()
|
|
{
|
|
for (ListElement * i = notYetSentUpReply.begin(); i!=notYetSentUpReply.end(); )
|
|
{
|
|
Message * m = (Message *)i;
|
|
i = i->next; // Do this first since we may unlink the current node
|
|
|
|
notYetSentUpReply.remove(m);
|
|
|
|
//
|
|
// This call can cause transmitAwaitingUpReplies to be called again
|
|
//
|
|
bool sent = splitterUpReply.send(m->encodedMessage, m);
|
|
DP_ASSERT(sent);
|
|
}
|
|
}
|
|
|
|
void MessageManager::postReply(Message * message, Message::MessageEventSink * sink)
|
|
{
|
|
post(message, sink, true);
|
|
}
|
|
|
|
void MessageManager::cancelAllByType(unsigned type)
|
|
{
|
|
for (ListElement * i = notYetSentDownRequest.begin(); i!=notYetSentDownRequest.end(); )
|
|
{
|
|
Message * m = (Message *)i;
|
|
i = i->next;
|
|
|
|
if (m->requestIdentifier == type)
|
|
notYetSentDownRequest.remove(m);
|
|
}
|
|
|
|
for (ListElement * i = awaitingReplyDownRequest.begin(); i!=awaitingReplyDownRequest.end(); )
|
|
{
|
|
Message * m = (Message *)i;
|
|
i = i->next;
|
|
|
|
if (m->requestIdentifier == type)
|
|
awaitingReplyDownRequest.remove(m);
|
|
}
|
|
}
|
|
|
|
void MessageManager::cancelAll(Message * message)
|
|
{
|
|
for (ListElement * i = notYetSentDownRequest.begin(); i!=notYetSentDownRequest.end(); )
|
|
{
|
|
Message * m = (Message *)i;
|
|
i = i->next;
|
|
|
|
if (m == message && m->requestIdentifier == message->requestIdentifier)
|
|
notYetSentDownRequest.remove(m);
|
|
}
|
|
|
|
for (ListElement * i = awaitingReplyDownRequest.begin(); i!=awaitingReplyDownRequest.end(); )
|
|
{
|
|
Message * m = (Message *)i;
|
|
i = i->next;
|
|
|
|
if (m == message && m->requestIdentifier == message->requestIdentifier)
|
|
awaitingReplyDownRequest.remove(m);
|
|
}
|
|
}
|
|
|
|
void MessageManager::post(Message * message, Message::MessageEventSink * sink, bool transmitReply)
|
|
{
|
|
DP_ASSERT(!isBeingDestroyed && "You may not post messages in response to a shutdown");
|
|
|
|
if (isPaused)
|
|
return;
|
|
|
|
//
|
|
// Initialize the fields
|
|
//
|
|
message->sink = sink;
|
|
message->bTransmitted = false;
|
|
|
|
//
|
|
// Queue the message for the outgoing queue.
|
|
// Later on we'll walk to the queue and make sure
|
|
// we have at most two outstanding messages PER
|
|
// target address. This is how the message
|
|
// number is decided.
|
|
//
|
|
|
|
message->parent = this;
|
|
message->transmitReply = transmitReply;
|
|
if (message->encodedMessage.isBroadcast)
|
|
{
|
|
// if its a broadcast message; the target would be the immediate branch.
|
|
Address addr;
|
|
addr.clear();
|
|
addr.append(0);
|
|
message->state.target = addr;
|
|
}
|
|
else
|
|
message->state.target = message->encodedMessage.address;
|
|
|
|
if ( transmitReply )
|
|
{
|
|
notYetSentUpReply.insertBack(message);
|
|
transmitAwaitingUpReplies();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If the list is empty or the incoming message has the least priority possible (DEFAULT priority),
|
|
// then just add the incoming message to the back of the list.
|
|
// Otherwise, find the right location by traversing the list.
|
|
//
|
|
if(message->messagePriority == NV_DP_SBMSG_PRIORITY_LEVEL_DEFAULT || notYetSentDownRequest.isEmpty())
|
|
{
|
|
notYetSentDownRequest.insertBack(message);
|
|
}
|
|
else
|
|
{
|
|
ListElement *tmp = notYetSentDownRequest.last();
|
|
Message *msg = (Message*) notYetSentDownRequest.last();
|
|
while((msg->prev != tmp) && (msg->messagePriority < message->messagePriority))
|
|
{
|
|
msg = (Message*)msg->prev;
|
|
}
|
|
notYetSentDownRequest.insertBefore(msg->next, message);
|
|
}
|
|
transmitAwaitingDownRequests();
|
|
}
|
|
}
|
|
|
|
void MessageManager::onUpRequestReceived(bool status, EncodedMessage * message)
|
|
{
|
|
if (!status)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Broadcast the up-request message to all
|
|
// the receivers on messageReceivers
|
|
//
|
|
for (ListElement * i = messageReceivers.begin(); i!=messageReceivers.end(); i=i->next)
|
|
{
|
|
MessageReceiver * rcr = (MessageReceiver *)i;
|
|
if (rcr->process((EncodedMessage *)message))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
DP_ASSERT(0 && "Warning: Unknown upstream UP_REQ message");
|
|
}
|
|
|
|
|
|
void MessageManager::onDownReplyReceived(bool status, EncodedMessage * message)
|
|
{
|
|
if (!status)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Broadcast the down-request message to all
|
|
// the receivers on awaitingReplyDownRequest
|
|
//
|
|
for (ListElement * i = awaitingReplyDownRequest.begin(); i!=awaitingReplyDownRequest.end(); i=i->next)
|
|
{
|
|
Message * messageAwaitingReply = (Message *)i;
|
|
|
|
if( messageAwaitingReply->state.target == message->address &&
|
|
messageAwaitingReply->state.messageNumber == message->messageNumber)
|
|
{
|
|
awaitingReplyDownRequest.remove(messageAwaitingReply);
|
|
if (messageAwaitingReply->parseResponse(message) == ParseResponseWrong)
|
|
{
|
|
//
|
|
// parseResponse() returns ParseResposeWrong when 'Request_Identifier' of down request
|
|
// message and down reply message are mis-matched. So insert message in waiting queue
|
|
// and wait for correct down reply message.
|
|
//
|
|
awaitingReplyDownRequest.insertBack(messageAwaitingReply);
|
|
}
|
|
|
|
goto nextMessage;
|
|
}
|
|
}
|
|
|
|
DP_LOG(("DPMM> Warning: Unmatched reply message"));
|
|
nextMessage:
|
|
transmitAwaitingUpReplies();
|
|
transmitAwaitingDownRequests();
|
|
}
|
|
|
|
MessageManager::~MessageManager()
|
|
{
|
|
// This causes any posts they may attempt to do to fail
|
|
isBeingDestroyed = true;
|
|
|
|
//
|
|
// The message manager should not be shut down until
|
|
// all outgoing messages are in the cancelled state
|
|
//
|
|
NakData nakUndef;
|
|
nakUndef.reason = NakUndefined;
|
|
|
|
for (ListElement * i = notYetSentDownRequest.begin(); i!=notYetSentDownRequest.end(); )
|
|
{
|
|
ListElement * next = i->next;
|
|
if (((Message *)i)->sink)
|
|
((Message *)i)->sink->messageFailed(((Message *)i), &nakUndef);
|
|
i = next;
|
|
}
|
|
if (!notYetSentDownRequest.isEmpty())
|
|
{
|
|
|
|
for (ListElement * i = notYetSentDownRequest.begin(); i!=notYetSentDownRequest.end(); )
|
|
{
|
|
ListElement * next = i->next;
|
|
DP_LOG(("Down request message type 0x%x client is not cleaning up.", ((Message *)i)->requestIdentifier));
|
|
i = next;
|
|
}
|
|
}
|
|
|
|
for (ListElement * i = notYetSentUpReply.begin(); i!=notYetSentUpReply.end();)
|
|
{
|
|
ListElement * next = i->next;
|
|
if (((Message *)i)->sink)
|
|
((Message *)i)->sink->messageFailed(((Message *)i), &nakUndef);
|
|
i = next;
|
|
}
|
|
if (!notYetSentUpReply.isEmpty())
|
|
{
|
|
|
|
for (ListElement * i = notYetSentUpReply.begin(); i!=notYetSentUpReply.end(); )
|
|
{
|
|
ListElement * next = i->next;
|
|
DP_LOG(("Up reply message type 0x%x client is not cleaning up.", ((Message *)i)->requestIdentifier));
|
|
i = next;
|
|
}
|
|
}
|
|
|
|
for (ListElement * i = awaitingReplyDownRequest.begin(); i!=awaitingReplyDownRequest.end(); )
|
|
{
|
|
ListElement * next = i->next;
|
|
if (((Message *)i)->sink)
|
|
((Message *)i)->sink->messageFailed(((Message *)i), &nakUndef);
|
|
i = next;
|
|
}
|
|
if (!awaitingReplyDownRequest.isEmpty())
|
|
{
|
|
|
|
for (ListElement * i = awaitingReplyDownRequest.begin(); i!=awaitingReplyDownRequest.end(); )
|
|
{
|
|
ListElement * next = i->next;
|
|
DP_LOG(("Down request message type 0x%x client is not cleaning up.", ((Message *)i)->requestIdentifier));
|
|
i = next;
|
|
}
|
|
}
|
|
|
|
// Do not reclaim the memory of our registered receivers
|
|
while (!messageReceivers.isEmpty())
|
|
messageReceivers.remove(messageReceivers.front());
|
|
}
|
|
|
|
ParseResponseStatus MessageManager::Message::parseResponse(EncodedMessage * message)
|
|
{
|
|
BitStreamReader reader(&message->buffer, 0, message->buffer.length*8);
|
|
|
|
// Read ReplyType
|
|
bool replyNacked = !!reader.readOrDefault(1, true);
|
|
|
|
// Read RequestIdentifier
|
|
unsigned requestId = reader.readOrDefault(7, 0);
|
|
if (requestId != requestIdentifier)
|
|
{
|
|
DP_LOG(("DP-MM> Requested = %x Received = %x", requestId, requestIdentifier));
|
|
DP_ASSERT(0 && "Reply type doesn't match");
|
|
return ParseResponseWrong;
|
|
}
|
|
|
|
if (replyNacked)
|
|
{
|
|
NakData nakData;
|
|
|
|
// failure handler will parse the NAK response and do the required action
|
|
if (DisplayPort::extractGUID(&reader, &nakData.guid) == false)
|
|
{
|
|
DP_ASSERT(0 && "Invalid GUID in NAK");
|
|
}
|
|
|
|
nakData.reason = (NakReason)reader.readOrDefault(8, 0);
|
|
nakData.nak_data = reader.readOrDefault(8, 0);
|
|
|
|
// call specific handler after parsing.
|
|
parent->timer->cancelCallbacks(this);
|
|
|
|
MessageManager * parent = this->parent;
|
|
|
|
if (sink)
|
|
sink->messageFailed(this, &nakData);
|
|
|
|
parent->transmitAwaitingDownRequests();
|
|
|
|
return ParseResponseSuccess;
|
|
}
|
|
|
|
ParseResponseStatus parseResult = parseResponseAck(message, &reader);
|
|
|
|
if (parseResult == ParseResponseSuccess)
|
|
{
|
|
parent->timer->cancelCallbacks(this);
|
|
|
|
if (this->sink)
|
|
{
|
|
MessageEventSink * msgSink = this->sink;
|
|
msgSink->messageCompleted(this);
|
|
}
|
|
}
|
|
|
|
return parseResult;
|
|
}
|
|
|
|
void MessageManager::Message::MessageEventSink::messageFailed(Message * from, NakData * nakData)
|
|
{
|
|
|
|
}
|
|
|
|
void MessageManager::registerReceiver(MessageReceiver * receiver)
|
|
{
|
|
messageReceivers.insertBack(receiver);
|
|
}
|
|
|
|
|
|
bool MessageManager::MessageReceiver::process(EncodedMessage * message)
|
|
{
|
|
BitStreamReader reader(&message->buffer, 0, message->buffer.length*8);
|
|
|
|
// Read RequestIdentifier
|
|
reader.readOrDefault(1, 0);
|
|
unsigned reqId = reader.readOrDefault(7, 0);
|
|
|
|
if (reqId != this->getRequestId())
|
|
{
|
|
//
|
|
// This receiver is not meant for this message;
|
|
// let the next in the queue handle it.
|
|
//
|
|
return false;
|
|
}
|
|
|
|
this->address = message->address;
|
|
|
|
// processByType should parse the request, create a response and queue it if needed
|
|
bool status = processByType(message, &reader);
|
|
if (!status)
|
|
{
|
|
//
|
|
// if we are here; we could get a receiver to handle the request
|
|
// but something else went wrong.
|
|
//
|
|
DP_ASSERT(0);
|
|
}
|
|
|
|
return true;
|
|
}
|