xref: /openbmc/obmc-ikvm/ikvm_server.cpp (revision 3c110335)
121b177e0SEddie James #include "ikvm_server.hpp"
221b177e0SEddie James 
38e909b75SCharles Kearney #include <linux/videodev2.h>
429f775abSEddie James #include <rfb/rfbproto.h>
529f775abSEddie James 
6f79f6f54SGeorge Liu #include <boost/crc.hpp>
729f775abSEddie James #include <phosphor-logging/elog-errors.hpp>
829f775abSEddie James #include <phosphor-logging/elog.hpp>
929f775abSEddie James #include <phosphor-logging/log.hpp>
1029f775abSEddie James #include <xyz/openbmc_project/Common/error.hpp>
1129f775abSEddie James 
1221b177e0SEddie James namespace ikvm
1321b177e0SEddie James {
1421b177e0SEddie James 
1529f775abSEddie James using namespace phosphor::logging;
1629f775abSEddie James using namespace sdbusplus::xyz::openbmc_project::Common::Error;
1729f775abSEddie James 
Server(const Args & args,Input & i,Video & v)1821b177e0SEddie James Server::Server(const Args& args, Input& i, Video& v) :
1929f775abSEddie James     pendingResize(false), frameCounter(0), numClients(0), input(i), video(v)
2021b177e0SEddie James {
2129f775abSEddie James     std::string ip("localhost");
2229f775abSEddie James     const Args::CommandLine& commandLine = args.getCommandLine();
2329f775abSEddie James     int argc = commandLine.argc;
2429f775abSEddie James 
2529f775abSEddie James     server = rfbGetScreen(&argc, commandLine.argv, video.getWidth(),
2629f775abSEddie James                           video.getHeight(), Video::bitsPerSample,
2729f775abSEddie James                           Video::samplesPerPixel, Video::bytesPerPixel);
2829f775abSEddie James 
2929f775abSEddie James     if (!server)
3029f775abSEddie James     {
3129f775abSEddie James         log<level::ERR>("Failed to get VNC screen due to invalid arguments");
3229f775abSEddie James         elog<InvalidArgument>(
3329f775abSEddie James             xyz::openbmc_project::Common::InvalidArgument::ARGUMENT_NAME(""),
3429f775abSEddie James             xyz::openbmc_project::Common::InvalidArgument::ARGUMENT_VALUE(""));
3529f775abSEddie James     }
3629f775abSEddie James 
3729f775abSEddie James     framebuffer.resize(
3829f775abSEddie James         video.getHeight() * video.getWidth() * Video::bytesPerPixel, 0);
3929f775abSEddie James 
4029f775abSEddie James     server->screenData = this;
4129f775abSEddie James     server->desktopName = "OpenBMC IKVM";
4229f775abSEddie James     server->frameBuffer = framebuffer.data();
4329f775abSEddie James     server->newClientHook = newClient;
447dfac9ffSJae Hyun Yoo     server->cursor = rfbMakeXCursor(cursorWidth, cursorHeight, (char*)cursor,
457dfac9ffSJae Hyun Yoo                                     (char*)cursorMask);
467dfac9ffSJae Hyun Yoo     server->cursor->xhot = 1;
477dfac9ffSJae Hyun Yoo     server->cursor->yhot = 1;
4829f775abSEddie James 
4929f775abSEddie James     rfbStringToAddr(&ip[0], &server->listenInterface);
5029f775abSEddie James 
5129f775abSEddie James     rfbInitServer(server);
5229f775abSEddie James 
5329f775abSEddie James     rfbMarkRectAsModified(server, 0, 0, video.getWidth(), video.getHeight());
5429f775abSEddie James 
5529f775abSEddie James     server->kbdAddEvent = Input::keyEvent;
5629f775abSEddie James     server->ptrAddEvent = Input::pointerEvent;
5729f775abSEddie James 
5829f775abSEddie James     processTime = (1000000 / video.getFrameRate()) - 100;
592d2f3dabSPaul Fertser 
602d2f3dabSPaul Fertser     calcFrameCRC = args.getCalcFrameCRC();
6121b177e0SEddie James }
6221b177e0SEddie James 
~Server()6321b177e0SEddie James Server::~Server()
6421b177e0SEddie James {
6529f775abSEddie James     rfbScreenCleanup(server);
6629f775abSEddie James }
6729f775abSEddie James 
resize()6829f775abSEddie James void Server::resize()
6929f775abSEddie James {
7029f775abSEddie James     if (frameCounter > video.getFrameRate())
7129f775abSEddie James     {
7229f775abSEddie James         doResize();
7329f775abSEddie James     }
7429f775abSEddie James     else
7529f775abSEddie James     {
7629f775abSEddie James         pendingResize = true;
7729f775abSEddie James     }
7829f775abSEddie James }
7929f775abSEddie James 
run()8029f775abSEddie James void Server::run()
8129f775abSEddie James {
8229f775abSEddie James     rfbProcessEvents(server, processTime);
8329f775abSEddie James 
8429f775abSEddie James     if (server->clientHead)
8529f775abSEddie James     {
8629f775abSEddie James         frameCounter++;
8729f775abSEddie James         if (pendingResize && frameCounter > video.getFrameRate())
8829f775abSEddie James         {
8929f775abSEddie James             doResize();
9029f775abSEddie James             pendingResize = false;
9129f775abSEddie James         }
9229f775abSEddie James     }
9329f775abSEddie James }
9429f775abSEddie James 
sendFrame()9529f775abSEddie James void Server::sendFrame()
9629f775abSEddie James {
9729f775abSEddie James     char* data = video.getData();
9829f775abSEddie James     rfbClientIteratorPtr it;
9929f775abSEddie James     rfbClientPtr cl;
1002d2f3dabSPaul Fertser     int64_t frame_crc = -1;
10129f775abSEddie James 
10229f775abSEddie James     if (!data || pendingResize)
10329f775abSEddie James     {
10429f775abSEddie James         return;
10529f775abSEddie James     }
10629f775abSEddie James 
10729f775abSEddie James     it = rfbGetClientIterator(server);
10829f775abSEddie James 
10929f775abSEddie James     while ((cl = rfbClientIteratorNext(it)))
11029f775abSEddie James     {
11129f775abSEddie James         ClientData* cd = (ClientData*)cl->clientData;
11229f775abSEddie James         rfbFramebufferUpdateMsg* fu = (rfbFramebufferUpdateMsg*)cl->updateBuf;
11329f775abSEddie James 
11429f775abSEddie James         if (!cd)
11529f775abSEddie James         {
11629f775abSEddie James             continue;
11729f775abSEddie James         }
11829f775abSEddie James 
11929f775abSEddie James         if (cd->skipFrame)
12029f775abSEddie James         {
12129f775abSEddie James             cd->skipFrame--;
12229f775abSEddie James             continue;
12329f775abSEddie James         }
12429f775abSEddie James 
12585d04552SJae Hyun Yoo         if (!cd->needUpdate)
12685d04552SJae Hyun Yoo         {
12785d04552SJae Hyun Yoo             continue;
12885d04552SJae Hyun Yoo         }
1292d2f3dabSPaul Fertser 
1302d2f3dabSPaul Fertser         if (calcFrameCRC)
1312d2f3dabSPaul Fertser         {
1322d2f3dabSPaul Fertser             if (frame_crc == -1)
1332d2f3dabSPaul Fertser             {
1342d2f3dabSPaul Fertser                 /* JFIF header contains some varying data so skip it for
1352d2f3dabSPaul Fertser                  * checksum calculation */
136*3c110335SPatrick Williams                 frame_crc = boost::crc<32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF,
137*3c110335SPatrick Williams                                        true, true>(data + 0x30,
138*3c110335SPatrick Williams                                                    video.getFrameSize() - 0x30);
1392d2f3dabSPaul Fertser             }
1402d2f3dabSPaul Fertser 
1412d2f3dabSPaul Fertser             if (cd->last_crc == frame_crc)
1422d2f3dabSPaul Fertser             {
1432d2f3dabSPaul Fertser                 continue;
1442d2f3dabSPaul Fertser             }
1452d2f3dabSPaul Fertser 
1462d2f3dabSPaul Fertser             cd->last_crc = frame_crc;
1472d2f3dabSPaul Fertser         }
1482d2f3dabSPaul Fertser 
14985d04552SJae Hyun Yoo         cd->needUpdate = false;
15085d04552SJae Hyun Yoo 
15129f775abSEddie James         if (cl->enableLastRectEncoding)
15229f775abSEddie James         {
15329f775abSEddie James             fu->nRects = 0xFFFF;
15429f775abSEddie James         }
15529f775abSEddie James         else
15629f775abSEddie James         {
15729f775abSEddie James             fu->nRects = Swap16IfLE(1);
15829f775abSEddie James         }
15929f775abSEddie James 
1608e909b75SCharles Kearney         switch (video.getPixelformat())
1618e909b75SCharles Kearney         {
1628e909b75SCharles Kearney             case V4L2_PIX_FMT_RGB24:
1638e909b75SCharles Kearney                 framebuffer.assign(data, data + video.getFrameSize());
1648e909b75SCharles Kearney                 rfbMarkRectAsModified(server, 0, 0, video.getWidth(),
1658e909b75SCharles Kearney                                       video.getHeight());
1668e909b75SCharles Kearney                 break;
1678e909b75SCharles Kearney 
1688e909b75SCharles Kearney             case V4L2_PIX_FMT_JPEG:
16929f775abSEddie James                 fu->type = rfbFramebufferUpdate;
17029f775abSEddie James                 cl->ublen = sz_rfbFramebufferUpdateMsg;
17129f775abSEddie James                 rfbSendUpdateBuf(cl);
17229f775abSEddie James                 cl->tightEncoding = rfbEncodingTight;
1738e909b75SCharles Kearney                 rfbSendTightHeader(cl, 0, 0, video.getWidth(),
1748e909b75SCharles Kearney                                    video.getHeight());
17529f775abSEddie James                 cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4);
17629f775abSEddie James                 rfbSendCompressedDataTight(cl, data, video.getFrameSize());
17729f775abSEddie James                 if (cl->enableLastRectEncoding)
17829f775abSEddie James                 {
17929f775abSEddie James                     rfbSendLastRectMarker(cl);
18029f775abSEddie James                 }
18129f775abSEddie James                 rfbSendUpdateBuf(cl);
1828e909b75SCharles Kearney                 break;
1838e909b75SCharles Kearney 
1848e909b75SCharles Kearney             default:
1858e909b75SCharles Kearney                 break;
1868e909b75SCharles Kearney         }
18729f775abSEddie James     }
18829f775abSEddie James 
18929f775abSEddie James     rfbReleaseClientIterator(it);
19029f775abSEddie James }
19129f775abSEddie James 
clientFramebufferUpdateRequest(rfbClientPtr cl,rfbFramebufferUpdateRequestMsg * furMsg)19285d04552SJae Hyun Yoo void Server::clientFramebufferUpdateRequest(
19385d04552SJae Hyun Yoo     rfbClientPtr cl, rfbFramebufferUpdateRequestMsg* furMsg)
19485d04552SJae Hyun Yoo {
19585d04552SJae Hyun Yoo     ClientData* cd = (ClientData*)cl->clientData;
19685d04552SJae Hyun Yoo 
19785d04552SJae Hyun Yoo     if (!cd)
19885d04552SJae Hyun Yoo         return;
19985d04552SJae Hyun Yoo 
20085d04552SJae Hyun Yoo     // Ignore the furMsg info. This service uses full frame update always.
20185d04552SJae Hyun Yoo     (void)furMsg;
20285d04552SJae Hyun Yoo 
20385d04552SJae Hyun Yoo     cd->needUpdate = true;
20485d04552SJae Hyun Yoo }
20585d04552SJae Hyun Yoo 
clientGone(rfbClientPtr cl)20629f775abSEddie James void Server::clientGone(rfbClientPtr cl)
20729f775abSEddie James {
20829f775abSEddie James     Server* server = (Server*)cl->screen->screenData;
20929f775abSEddie James 
21029f775abSEddie James     delete (ClientData*)cl->clientData;
2110049bfacSJae Hyun Yoo     cl->clientData = nullptr;
21229f775abSEddie James 
21329f775abSEddie James     if (server->numClients-- == 1)
21429f775abSEddie James     {
215c11257d8SJae Hyun Yoo         server->input.disconnect();
21629f775abSEddie James         rfbMarkRectAsModified(server->server, 0, 0, server->video.getWidth(),
21729f775abSEddie James                               server->video.getHeight());
21829f775abSEddie James     }
21929f775abSEddie James }
22029f775abSEddie James 
newClient(rfbClientPtr cl)22129f775abSEddie James enum rfbNewClientAction Server::newClient(rfbClientPtr cl)
22229f775abSEddie James {
22329f775abSEddie James     Server* server = (Server*)cl->screen->screenData;
22429f775abSEddie James 
225*3c110335SPatrick Williams     cl->clientData = new ClientData(server->video.getFrameRate(),
226*3c110335SPatrick Williams                                     &server->input);
22729f775abSEddie James     cl->clientGoneHook = clientGone;
22885d04552SJae Hyun Yoo     cl->clientFramebufferUpdateRequestHook = clientFramebufferUpdateRequest;
22929f775abSEddie James     if (!server->numClients++)
23029f775abSEddie James     {
231c11257d8SJae Hyun Yoo         server->input.connect();
23229f775abSEddie James         server->pendingResize = false;
23329f775abSEddie James         server->frameCounter = 0;
23429f775abSEddie James     }
23529f775abSEddie James 
23629f775abSEddie James     return RFB_CLIENT_ACCEPT;
23729f775abSEddie James }
23829f775abSEddie James 
doResize()23929f775abSEddie James void Server::doResize()
24029f775abSEddie James {
24129f775abSEddie James     rfbClientIteratorPtr it;
24229f775abSEddie James     rfbClientPtr cl;
24329f775abSEddie James 
24429f775abSEddie James     framebuffer.resize(
24529f775abSEddie James         video.getHeight() * video.getWidth() * Video::bytesPerPixel, 0);
24629f775abSEddie James 
24729f775abSEddie James     rfbNewFramebuffer(server, framebuffer.data(), video.getWidth(),
24829f775abSEddie James                       video.getHeight(), Video::bitsPerSample,
24929f775abSEddie James                       Video::samplesPerPixel, Video::bytesPerPixel);
25029f775abSEddie James     rfbMarkRectAsModified(server, 0, 0, video.getWidth(), video.getHeight());
25129f775abSEddie James 
25229f775abSEddie James     it = rfbGetClientIterator(server);
25329f775abSEddie James 
25429f775abSEddie James     while ((cl = rfbClientIteratorNext(it)))
25529f775abSEddie James     {
25629f775abSEddie James         ClientData* cd = (ClientData*)cl->clientData;
25729f775abSEddie James 
25829f775abSEddie James         if (!cd)
25929f775abSEddie James         {
26029f775abSEddie James             continue;
26129f775abSEddie James         }
26229f775abSEddie James 
26329f775abSEddie James         // delay video updates to give the client time to resize
26429f775abSEddie James         cd->skipFrame = video.getFrameRate();
26529f775abSEddie James     }
26629f775abSEddie James 
26729f775abSEddie James     rfbReleaseClientIterator(it);
26821b177e0SEddie James }
26921b177e0SEddie James 
27021b177e0SEddie James } // namespace ikvm
271