1 #include "ikvm_server.hpp" 2 3 #include <linux/videodev2.h> 4 #include <rfb/rfbproto.h> 5 6 #include <boost/crc.hpp> 7 #include <phosphor-logging/elog-errors.hpp> 8 #include <phosphor-logging/elog.hpp> 9 #include <phosphor-logging/log.hpp> 10 #include <xyz/openbmc_project/Common/error.hpp> 11 12 namespace ikvm 13 { 14 15 using namespace phosphor::logging; 16 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 17 18 Server::Server(const Args& args, Input& i, Video& v) : 19 pendingResize(false), frameCounter(0), numClients(0), input(i), video(v) 20 { 21 std::string ip("localhost"); 22 const Args::CommandLine& commandLine = args.getCommandLine(); 23 int argc = commandLine.argc; 24 25 server = rfbGetScreen(&argc, commandLine.argv, video.getWidth(), 26 video.getHeight(), Video::bitsPerSample, 27 Video::samplesPerPixel, Video::bytesPerPixel); 28 29 if (!server) 30 { 31 log<level::ERR>("Failed to get VNC screen due to invalid arguments"); 32 elog<InvalidArgument>( 33 xyz::openbmc_project::Common::InvalidArgument::ARGUMENT_NAME(""), 34 xyz::openbmc_project::Common::InvalidArgument::ARGUMENT_VALUE("")); 35 } 36 37 framebuffer.resize( 38 video.getHeight() * video.getWidth() * Video::bytesPerPixel, 0); 39 40 server->screenData = this; 41 server->desktopName = "OpenBMC IKVM"; 42 server->frameBuffer = framebuffer.data(); 43 server->newClientHook = newClient; 44 server->cursor = rfbMakeXCursor(cursorWidth, cursorHeight, (char*)cursor, 45 (char*)cursorMask); 46 server->cursor->xhot = 1; 47 server->cursor->yhot = 1; 48 49 rfbStringToAddr(&ip[0], &server->listenInterface); 50 51 rfbInitServer(server); 52 53 rfbMarkRectAsModified(server, 0, 0, video.getWidth(), video.getHeight()); 54 55 server->kbdAddEvent = Input::keyEvent; 56 server->ptrAddEvent = Input::pointerEvent; 57 58 processTime = (1000000 / video.getFrameRate()) - 100; 59 60 calcFrameCRC = args.getCalcFrameCRC(); 61 } 62 63 Server::~Server() 64 { 65 rfbScreenCleanup(server); 66 } 67 68 void Server::resize() 69 { 70 if (frameCounter > video.getFrameRate()) 71 { 72 doResize(); 73 } 74 else 75 { 76 pendingResize = true; 77 } 78 } 79 80 void Server::run() 81 { 82 rfbProcessEvents(server, processTime); 83 84 if (server->clientHead) 85 { 86 frameCounter++; 87 if (pendingResize && frameCounter > video.getFrameRate()) 88 { 89 doResize(); 90 pendingResize = false; 91 } 92 } 93 } 94 95 void Server::sendFrame() 96 { 97 char* data = video.getData(); 98 rfbClientIteratorPtr it; 99 rfbClientPtr cl; 100 int64_t frame_crc = -1; 101 102 if (!data || pendingResize) 103 { 104 return; 105 } 106 107 it = rfbGetClientIterator(server); 108 109 while ((cl = rfbClientIteratorNext(it))) 110 { 111 ClientData* cd = (ClientData*)cl->clientData; 112 rfbFramebufferUpdateMsg* fu = (rfbFramebufferUpdateMsg*)cl->updateBuf; 113 114 if (!cd) 115 { 116 continue; 117 } 118 119 if (cd->skipFrame) 120 { 121 cd->skipFrame--; 122 continue; 123 } 124 125 if (!cd->needUpdate) 126 { 127 continue; 128 } 129 130 if (calcFrameCRC) 131 { 132 if (frame_crc == -1) 133 { 134 /* JFIF header contains some varying data so skip it for 135 * checksum calculation */ 136 frame_crc = boost::crc<32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, 137 true, true>(data + 0x30, 138 video.getFrameSize() - 0x30); 139 } 140 141 if (cd->last_crc == frame_crc) 142 { 143 continue; 144 } 145 146 cd->last_crc = frame_crc; 147 } 148 149 cd->needUpdate = false; 150 151 if (cl->enableLastRectEncoding) 152 { 153 fu->nRects = 0xFFFF; 154 } 155 else 156 { 157 fu->nRects = Swap16IfLE(1); 158 } 159 160 switch (video.getPixelformat()) 161 { 162 case V4L2_PIX_FMT_RGB24: 163 framebuffer.assign(data, data + video.getFrameSize()); 164 rfbMarkRectAsModified(server, 0, 0, video.getWidth(), 165 video.getHeight()); 166 break; 167 168 case V4L2_PIX_FMT_JPEG: 169 fu->type = rfbFramebufferUpdate; 170 cl->ublen = sz_rfbFramebufferUpdateMsg; 171 rfbSendUpdateBuf(cl); 172 cl->tightEncoding = rfbEncodingTight; 173 rfbSendTightHeader(cl, 0, 0, video.getWidth(), 174 video.getHeight()); 175 cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4); 176 rfbSendCompressedDataTight(cl, data, video.getFrameSize()); 177 if (cl->enableLastRectEncoding) 178 { 179 rfbSendLastRectMarker(cl); 180 } 181 rfbSendUpdateBuf(cl); 182 break; 183 184 default: 185 break; 186 } 187 } 188 189 rfbReleaseClientIterator(it); 190 } 191 192 void Server::clientFramebufferUpdateRequest( 193 rfbClientPtr cl, rfbFramebufferUpdateRequestMsg* furMsg) 194 { 195 ClientData* cd = (ClientData*)cl->clientData; 196 197 if (!cd) 198 return; 199 200 // Ignore the furMsg info. This service uses full frame update always. 201 (void)furMsg; 202 203 cd->needUpdate = true; 204 } 205 206 void Server::clientGone(rfbClientPtr cl) 207 { 208 Server* server = (Server*)cl->screen->screenData; 209 210 delete (ClientData*)cl->clientData; 211 cl->clientData = nullptr; 212 213 if (server->numClients-- == 1) 214 { 215 server->input.disconnect(); 216 rfbMarkRectAsModified(server->server, 0, 0, server->video.getWidth(), 217 server->video.getHeight()); 218 } 219 } 220 221 enum rfbNewClientAction Server::newClient(rfbClientPtr cl) 222 { 223 Server* server = (Server*)cl->screen->screenData; 224 225 cl->clientData = new ClientData(server->video.getFrameRate(), 226 &server->input); 227 cl->clientGoneHook = clientGone; 228 cl->clientFramebufferUpdateRequestHook = clientFramebufferUpdateRequest; 229 if (!server->numClients++) 230 { 231 server->input.connect(); 232 server->pendingResize = false; 233 server->frameCounter = 0; 234 } 235 236 return RFB_CLIENT_ACCEPT; 237 } 238 239 void Server::doResize() 240 { 241 rfbClientIteratorPtr it; 242 rfbClientPtr cl; 243 244 framebuffer.resize( 245 video.getHeight() * video.getWidth() * Video::bytesPerPixel, 0); 246 247 rfbNewFramebuffer(server, framebuffer.data(), video.getWidth(), 248 video.getHeight(), Video::bitsPerSample, 249 Video::samplesPerPixel, Video::bytesPerPixel); 250 rfbMarkRectAsModified(server, 0, 0, video.getWidth(), video.getHeight()); 251 252 it = rfbGetClientIterator(server); 253 254 while ((cl = rfbClientIteratorNext(it))) 255 { 256 ClientData* cd = (ClientData*)cl->clientData; 257 258 if (!cd) 259 { 260 continue; 261 } 262 263 // delay video updates to give the client time to resize 264 cd->skipFrame = video.getFrameRate(); 265 } 266 267 rfbReleaseClientIterator(it); 268 } 269 270 } // namespace ikvm 271