xref: /openbmc/obmc-ikvm/ikvm_server.cpp (revision f79f6f54)
121b177e0SEddie James #include "ikvm_server.hpp"
221b177e0SEddie James 
329f775abSEddie James #include <rfb/rfbproto.h>
429f775abSEddie James 
5*f79f6f54SGeorge Liu #include <boost/crc.hpp>
629f775abSEddie James #include <phosphor-logging/elog-errors.hpp>
729f775abSEddie James #include <phosphor-logging/elog.hpp>
829f775abSEddie James #include <phosphor-logging/log.hpp>
929f775abSEddie James #include <xyz/openbmc_project/Common/error.hpp>
1029f775abSEddie James 
1121b177e0SEddie James namespace ikvm
1221b177e0SEddie James {
1321b177e0SEddie James 
1429f775abSEddie James using namespace phosphor::logging;
1529f775abSEddie James using namespace sdbusplus::xyz::openbmc_project::Common::Error;
1629f775abSEddie James 
1721b177e0SEddie James Server::Server(const Args& args, Input& i, Video& v) :
1829f775abSEddie James     pendingResize(false), frameCounter(0), numClients(0), input(i), video(v)
1921b177e0SEddie James {
2029f775abSEddie James     std::string ip("localhost");
2129f775abSEddie James     const Args::CommandLine& commandLine = args.getCommandLine();
2229f775abSEddie James     int argc = commandLine.argc;
2329f775abSEddie James 
2429f775abSEddie James     server = rfbGetScreen(&argc, commandLine.argv, video.getWidth(),
2529f775abSEddie James                           video.getHeight(), Video::bitsPerSample,
2629f775abSEddie James                           Video::samplesPerPixel, Video::bytesPerPixel);
2729f775abSEddie James 
2829f775abSEddie James     if (!server)
2929f775abSEddie James     {
3029f775abSEddie James         log<level::ERR>("Failed to get VNC screen due to invalid arguments");
3129f775abSEddie James         elog<InvalidArgument>(
3229f775abSEddie James             xyz::openbmc_project::Common::InvalidArgument::ARGUMENT_NAME(""),
3329f775abSEddie James             xyz::openbmc_project::Common::InvalidArgument::ARGUMENT_VALUE(""));
3429f775abSEddie James     }
3529f775abSEddie James 
3629f775abSEddie James     framebuffer.resize(
3729f775abSEddie James         video.getHeight() * video.getWidth() * Video::bytesPerPixel, 0);
3829f775abSEddie James 
3929f775abSEddie James     server->screenData = this;
4029f775abSEddie James     server->desktopName = "OpenBMC IKVM";
4129f775abSEddie James     server->frameBuffer = framebuffer.data();
4229f775abSEddie James     server->newClientHook = newClient;
437dfac9ffSJae Hyun Yoo     server->cursor = rfbMakeXCursor(cursorWidth, cursorHeight, (char*)cursor,
447dfac9ffSJae Hyun Yoo                                     (char*)cursorMask);
457dfac9ffSJae Hyun Yoo     server->cursor->xhot = 1;
467dfac9ffSJae Hyun Yoo     server->cursor->yhot = 1;
4729f775abSEddie James 
4829f775abSEddie James     rfbStringToAddr(&ip[0], &server->listenInterface);
4929f775abSEddie James 
5029f775abSEddie James     rfbInitServer(server);
5129f775abSEddie James 
5229f775abSEddie James     rfbMarkRectAsModified(server, 0, 0, video.getWidth(), video.getHeight());
5329f775abSEddie James 
5429f775abSEddie James     server->kbdAddEvent = Input::keyEvent;
5529f775abSEddie James     server->ptrAddEvent = Input::pointerEvent;
5629f775abSEddie James 
5729f775abSEddie James     processTime = (1000000 / video.getFrameRate()) - 100;
582d2f3dabSPaul Fertser 
592d2f3dabSPaul Fertser     calcFrameCRC = args.getCalcFrameCRC();
6021b177e0SEddie James }
6121b177e0SEddie James 
6221b177e0SEddie James Server::~Server()
6321b177e0SEddie James {
6429f775abSEddie James     rfbScreenCleanup(server);
6529f775abSEddie James }
6629f775abSEddie James 
6729f775abSEddie James void Server::resize()
6829f775abSEddie James {
6929f775abSEddie James     if (frameCounter > video.getFrameRate())
7029f775abSEddie James     {
7129f775abSEddie James         doResize();
7229f775abSEddie James     }
7329f775abSEddie James     else
7429f775abSEddie James     {
7529f775abSEddie James         pendingResize = true;
7629f775abSEddie James     }
7729f775abSEddie James }
7829f775abSEddie James 
7929f775abSEddie James void Server::run()
8029f775abSEddie James {
8129f775abSEddie James     rfbProcessEvents(server, processTime);
8229f775abSEddie James 
8329f775abSEddie James     if (server->clientHead)
8429f775abSEddie James     {
8529f775abSEddie James         frameCounter++;
8629f775abSEddie James         if (pendingResize && frameCounter > video.getFrameRate())
8729f775abSEddie James         {
8829f775abSEddie James             doResize();
8929f775abSEddie James             pendingResize = false;
9029f775abSEddie James         }
9129f775abSEddie James     }
9229f775abSEddie James }
9329f775abSEddie James 
9429f775abSEddie James void Server::sendFrame()
9529f775abSEddie James {
9629f775abSEddie James     char* data = video.getData();
9729f775abSEddie James     rfbClientIteratorPtr it;
9829f775abSEddie James     rfbClientPtr cl;
992d2f3dabSPaul Fertser     int64_t frame_crc = -1;
10029f775abSEddie James 
10129f775abSEddie James     if (!data || pendingResize)
10229f775abSEddie James     {
10329f775abSEddie James         return;
10429f775abSEddie James     }
10529f775abSEddie James 
10629f775abSEddie James     it = rfbGetClientIterator(server);
10729f775abSEddie James 
10829f775abSEddie James     while ((cl = rfbClientIteratorNext(it)))
10929f775abSEddie James     {
11029f775abSEddie James         ClientData* cd = (ClientData*)cl->clientData;
11129f775abSEddie James         rfbFramebufferUpdateMsg* fu = (rfbFramebufferUpdateMsg*)cl->updateBuf;
11229f775abSEddie James 
11329f775abSEddie James         if (!cd)
11429f775abSEddie James         {
11529f775abSEddie James             continue;
11629f775abSEddie James         }
11729f775abSEddie James 
11829f775abSEddie James         if (cd->skipFrame)
11929f775abSEddie James         {
12029f775abSEddie James             cd->skipFrame--;
12129f775abSEddie James             continue;
12229f775abSEddie James         }
12329f775abSEddie James 
12485d04552SJae Hyun Yoo         if (!cd->needUpdate)
12585d04552SJae Hyun Yoo         {
12685d04552SJae Hyun Yoo             continue;
12785d04552SJae Hyun Yoo         }
1282d2f3dabSPaul Fertser 
1292d2f3dabSPaul Fertser         if (calcFrameCRC)
1302d2f3dabSPaul Fertser         {
1312d2f3dabSPaul Fertser             if (frame_crc == -1)
1322d2f3dabSPaul Fertser             {
1332d2f3dabSPaul Fertser                 /* JFIF header contains some varying data so skip it for
1342d2f3dabSPaul Fertser                  * checksum calculation */
135*f79f6f54SGeorge Liu                 frame_crc =
136*f79f6f54SGeorge Liu                     boost::crc<32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true,
137*f79f6f54SGeorge Liu                                true>(data + 0x30, video.getFrameSize() - 0x30);
1382d2f3dabSPaul Fertser             }
1392d2f3dabSPaul Fertser 
1402d2f3dabSPaul Fertser             if (cd->last_crc == frame_crc)
1412d2f3dabSPaul Fertser             {
1422d2f3dabSPaul Fertser                 continue;
1432d2f3dabSPaul Fertser             }
1442d2f3dabSPaul Fertser 
1452d2f3dabSPaul Fertser             cd->last_crc = frame_crc;
1462d2f3dabSPaul Fertser         }
1472d2f3dabSPaul Fertser 
14885d04552SJae Hyun Yoo         cd->needUpdate = false;
14985d04552SJae Hyun Yoo 
15029f775abSEddie James         if (cl->enableLastRectEncoding)
15129f775abSEddie James         {
15229f775abSEddie James             fu->nRects = 0xFFFF;
15329f775abSEddie James         }
15429f775abSEddie James         else
15529f775abSEddie James         {
15629f775abSEddie James             fu->nRects = Swap16IfLE(1);
15729f775abSEddie James         }
15829f775abSEddie James 
15929f775abSEddie James         fu->type = rfbFramebufferUpdate;
16029f775abSEddie James         cl->ublen = sz_rfbFramebufferUpdateMsg;
16129f775abSEddie James         rfbSendUpdateBuf(cl);
16229f775abSEddie James 
16329f775abSEddie James         cl->tightEncoding = rfbEncodingTight;
16429f775abSEddie James         rfbSendTightHeader(cl, 0, 0, video.getWidth(), video.getHeight());
16529f775abSEddie James 
16629f775abSEddie James         cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4);
16729f775abSEddie James         rfbSendCompressedDataTight(cl, data, video.getFrameSize());
16829f775abSEddie James 
16929f775abSEddie James         if (cl->enableLastRectEncoding)
17029f775abSEddie James         {
17129f775abSEddie James             rfbSendLastRectMarker(cl);
17229f775abSEddie James         }
17329f775abSEddie James 
17429f775abSEddie James         rfbSendUpdateBuf(cl);
17529f775abSEddie James     }
17629f775abSEddie James 
17729f775abSEddie James     rfbReleaseClientIterator(it);
17829f775abSEddie James }
17929f775abSEddie James 
18085d04552SJae Hyun Yoo void Server::clientFramebufferUpdateRequest(
18185d04552SJae Hyun Yoo     rfbClientPtr cl, rfbFramebufferUpdateRequestMsg* furMsg)
18285d04552SJae Hyun Yoo {
18385d04552SJae Hyun Yoo     ClientData* cd = (ClientData*)cl->clientData;
18485d04552SJae Hyun Yoo 
18585d04552SJae Hyun Yoo     if (!cd)
18685d04552SJae Hyun Yoo         return;
18785d04552SJae Hyun Yoo 
18885d04552SJae Hyun Yoo     // Ignore the furMsg info. This service uses full frame update always.
18985d04552SJae Hyun Yoo     (void)furMsg;
19085d04552SJae Hyun Yoo 
19185d04552SJae Hyun Yoo     cd->needUpdate = true;
19285d04552SJae Hyun Yoo }
19385d04552SJae Hyun Yoo 
19429f775abSEddie James void Server::clientGone(rfbClientPtr cl)
19529f775abSEddie James {
19629f775abSEddie James     Server* server = (Server*)cl->screen->screenData;
19729f775abSEddie James 
19829f775abSEddie James     delete (ClientData*)cl->clientData;
1990049bfacSJae Hyun Yoo     cl->clientData = nullptr;
20029f775abSEddie James 
20129f775abSEddie James     if (server->numClients-- == 1)
20229f775abSEddie James     {
203c11257d8SJae Hyun Yoo         server->input.disconnect();
20429f775abSEddie James         rfbMarkRectAsModified(server->server, 0, 0, server->video.getWidth(),
20529f775abSEddie James                               server->video.getHeight());
20629f775abSEddie James     }
20729f775abSEddie James }
20829f775abSEddie James 
20929f775abSEddie James enum rfbNewClientAction Server::newClient(rfbClientPtr cl)
21029f775abSEddie James {
21129f775abSEddie James     Server* server = (Server*)cl->screen->screenData;
21229f775abSEddie James 
21329f775abSEddie James     cl->clientData =
21429f775abSEddie James         new ClientData(server->video.getFrameRate(), &server->input);
21529f775abSEddie James     cl->clientGoneHook = clientGone;
21685d04552SJae Hyun Yoo     cl->clientFramebufferUpdateRequestHook = clientFramebufferUpdateRequest;
21729f775abSEddie James     if (!server->numClients++)
21829f775abSEddie James     {
219c11257d8SJae Hyun Yoo         server->input.connect();
22029f775abSEddie James         server->pendingResize = false;
22129f775abSEddie James         server->frameCounter = 0;
22229f775abSEddie James     }
22329f775abSEddie James 
22429f775abSEddie James     return RFB_CLIENT_ACCEPT;
22529f775abSEddie James }
22629f775abSEddie James 
22729f775abSEddie James void Server::doResize()
22829f775abSEddie James {
22929f775abSEddie James     rfbClientIteratorPtr it;
23029f775abSEddie James     rfbClientPtr cl;
23129f775abSEddie James 
23229f775abSEddie James     framebuffer.resize(
23329f775abSEddie James         video.getHeight() * video.getWidth() * Video::bytesPerPixel, 0);
23429f775abSEddie James 
23529f775abSEddie James     rfbNewFramebuffer(server, framebuffer.data(), video.getWidth(),
23629f775abSEddie James                       video.getHeight(), Video::bitsPerSample,
23729f775abSEddie James                       Video::samplesPerPixel, Video::bytesPerPixel);
23829f775abSEddie James     rfbMarkRectAsModified(server, 0, 0, video.getWidth(), video.getHeight());
23929f775abSEddie James 
24029f775abSEddie James     it = rfbGetClientIterator(server);
24129f775abSEddie James 
24229f775abSEddie James     while ((cl = rfbClientIteratorNext(it)))
24329f775abSEddie James     {
24429f775abSEddie James         ClientData* cd = (ClientData*)cl->clientData;
24529f775abSEddie James 
24629f775abSEddie James         if (!cd)
24729f775abSEddie James         {
24829f775abSEddie James             continue;
24929f775abSEddie James         }
25029f775abSEddie James 
25129f775abSEddie James         // delay video updates to give the client time to resize
25229f775abSEddie James         cd->skipFrame = video.getFrameRate();
25329f775abSEddie James     }
25429f775abSEddie James 
25529f775abSEddie James     rfbReleaseClientIterator(it);
25621b177e0SEddie James }
25721b177e0SEddie James 
25821b177e0SEddie James } // namespace ikvm
259