blob: 9274a9be50660353793869cf54e5100457eebd0f [file] [log] [blame]
Eddie James21b177e2018-12-11 13:14:46 -06001#include "ikvm_server.hpp"
2
Charles Kearney8e909b72023-03-29 21:11:23 +00003#include <linux/videodev2.h>
Eddie James29f775a2018-12-11 13:22:54 -06004#include <rfb/rfbproto.h>
5
George Liuf79f6f52022-07-06 09:32:35 +08006#include <boost/crc.hpp>
Eddie James29f775a2018-12-11 13:22:54 -06007#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
Eddie James21b177e2018-12-11 13:14:46 -060012namespace ikvm
13{
14
Eddie James29f775a2018-12-11 13:22:54 -060015using namespace phosphor::logging;
16using namespace sdbusplus::xyz::openbmc_project::Common::Error;
17
Eddie James21b177e2018-12-11 13:14:46 -060018Server::Server(const Args& args, Input& i, Video& v) :
Eddie James29f775a2018-12-11 13:22:54 -060019 pendingResize(false), frameCounter(0), numClients(0), input(i), video(v)
Eddie James21b177e2018-12-11 13:14:46 -060020{
Eddie James29f775a2018-12-11 13:22:54 -060021 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;
Jae Hyun Yoo7dfac9f2019-01-15 10:14:59 -080044 server->cursor = rfbMakeXCursor(cursorWidth, cursorHeight, (char*)cursor,
45 (char*)cursorMask);
46 server->cursor->xhot = 1;
47 server->cursor->yhot = 1;
Eddie James29f775a2018-12-11 13:22:54 -060048
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;
Paul Fertser2d2f3da2021-06-18 11:16:43 +000059
60 calcFrameCRC = args.getCalcFrameCRC();
Eddie James21b177e2018-12-11 13:14:46 -060061}
62
63Server::~Server()
64{
Eddie James29f775a2018-12-11 13:22:54 -060065 rfbScreenCleanup(server);
66}
67
68void Server::resize()
69{
70 if (frameCounter > video.getFrameRate())
71 {
72 doResize();
73 }
74 else
75 {
76 pendingResize = true;
77 }
78}
79
80void Server::run()
81{
82 rfbProcessEvents(server, processTime);
83
84 if (server->clientHead)
85 {
Eddie James29f775a2018-12-11 13:22:54 -060086 frameCounter++;
87 if (pendingResize && frameCounter > video.getFrameRate())
88 {
89 doResize();
90 pendingResize = false;
91 }
92 }
93}
94
95void Server::sendFrame()
96{
97 char* data = video.getData();
98 rfbClientIteratorPtr it;
99 rfbClientPtr cl;
Paul Fertser2d2f3da2021-06-18 11:16:43 +0000100 int64_t frame_crc = -1;
Eddie James29f775a2018-12-11 13:22:54 -0600101
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
Jae Hyun Yoo85d04552019-05-09 16:26:53 -0700125 if (!cd->needUpdate)
126 {
127 continue;
128 }
Paul Fertser2d2f3da2021-06-18 11:16:43 +0000129
130 if (calcFrameCRC)
131 {
132 if (frame_crc == -1)
133 {
134 /* JFIF header contains some varying data so skip it for
135 * checksum calculation */
Patrick Williams94f5f422024-08-16 15:22:08 -0400136 frame_crc =
137 boost::crc<32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true,
138 true>(data + 0x30, video.getFrameSize() - 0x30);
Paul Fertser2d2f3da2021-06-18 11:16:43 +0000139 }
140
141 if (cd->last_crc == frame_crc)
142 {
143 continue;
144 }
145
146 cd->last_crc = frame_crc;
147 }
148
Jae Hyun Yoo85d04552019-05-09 16:26:53 -0700149 cd->needUpdate = false;
150
Eddie James29f775a2018-12-11 13:22:54 -0600151 if (cl->enableLastRectEncoding)
152 {
153 fu->nRects = 0xFFFF;
154 }
155 else
156 {
157 fu->nRects = Swap16IfLE(1);
158 }
159
Charles Kearney8e909b72023-03-29 21:11:23 +0000160 switch (video.getPixelformat())
Eddie James29f775a2018-12-11 13:22:54 -0600161 {
Charles Kearney8e909b72023-03-29 21:11:23 +0000162 case V4L2_PIX_FMT_RGB24:
163 framebuffer.assign(data, data + video.getFrameSize());
164 rfbMarkRectAsModified(server, 0, 0, video.getWidth(),
165 video.getHeight());
166 break;
Eddie James29f775a2018-12-11 13:22:54 -0600167
Charles Kearney8e909b72023-03-29 21:11:23 +0000168 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 }
Eddie James29f775a2018-12-11 13:22:54 -0600187 }
188
189 rfbReleaseClientIterator(it);
190}
191
Jae Hyun Yoo85d04552019-05-09 16:26:53 -0700192void Server::clientFramebufferUpdateRequest(
George Liuf79f6f52022-07-06 09:32:35 +0800193 rfbClientPtr cl, rfbFramebufferUpdateRequestMsg* furMsg)
Jae Hyun Yoo85d04552019-05-09 16:26:53 -0700194{
George Liuf79f6f52022-07-06 09:32:35 +0800195 ClientData* cd = (ClientData*)cl->clientData;
Jae Hyun Yoo85d04552019-05-09 16:26:53 -0700196
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
Eddie James29f775a2018-12-11 13:22:54 -0600206void Server::clientGone(rfbClientPtr cl)
207{
208 Server* server = (Server*)cl->screen->screenData;
209
210 delete (ClientData*)cl->clientData;
Jae Hyun Yoo0049bfa2019-03-06 15:39:58 -0800211 cl->clientData = nullptr;
Eddie James29f775a2018-12-11 13:22:54 -0600212
213 if (server->numClients-- == 1)
214 {
Jae Hyun Yooc11257d2020-07-22 23:39:18 -0700215 server->input.disconnect();
Eddie James29f775a2018-12-11 13:22:54 -0600216 rfbMarkRectAsModified(server->server, 0, 0, server->video.getWidth(),
217 server->video.getHeight());
218 }
219}
220
221enum rfbNewClientAction Server::newClient(rfbClientPtr cl)
222{
223 Server* server = (Server*)cl->screen->screenData;
224
Patrick Williams94f5f422024-08-16 15:22:08 -0400225 cl->clientData =
226 new ClientData(server->video.getFrameRate(), &server->input);
Eddie James29f775a2018-12-11 13:22:54 -0600227 cl->clientGoneHook = clientGone;
Jae Hyun Yoo85d04552019-05-09 16:26:53 -0700228 cl->clientFramebufferUpdateRequestHook = clientFramebufferUpdateRequest;
Eddie James29f775a2018-12-11 13:22:54 -0600229 if (!server->numClients++)
230 {
Jae Hyun Yooc11257d2020-07-22 23:39:18 -0700231 server->input.connect();
Eddie James29f775a2018-12-11 13:22:54 -0600232 server->pendingResize = false;
233 server->frameCounter = 0;
Eddie James29f775a2018-12-11 13:22:54 -0600234 }
235
236 return RFB_CLIENT_ACCEPT;
237}
238
239void 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);
Eddie James21b177e2018-12-11 13:14:46 -0600268}
269
270} // namespace ikvm