2 * Remote framebuffer server
3 * NetWatch system management mode administration console
5 * Copyright (c) 2008 Jacob Potter and Joshua Wise. All rights reserved.
6 * This program is free software; you can redistribute and/or modify it under
7 * the terms found in the file LICENSE in the root of this source tree.
19 #include "lwip/stats.h"
23 #define SET_PIXEL_FORMAT 0
24 #define SET_ENCODINGS 2
25 #define FB_UPDATE_REQUEST 3
27 #define POINTER_EVENT 5
28 #define CLIENT_CUT_TEXT 6
30 #define RFB_BUF_SIZE 1536
32 #define SCREEN_CHUNKS_X 8
33 #define SCREEN_CHUNKS_Y 8
49 struct server_init_message {
52 struct pixel_format fmt;
57 struct fb_update_req {
73 struct key_event_pkt {
80 struct pointer_event_pkt {
87 struct text_event_pkt {
94 struct update_header {
114 char data[RFB_BUF_SIZE];
118 char next_update_incremental;
119 char update_requested;
121 struct fb_update_req client_interest_area;
129 uint32_t checksums[SCREEN_CHUNKS_X][SCREEN_CHUNKS_Y];
135 uint32_t chunk_width;
136 uint32_t chunk_height;
138 uint32_t chunk_bytes_sent;
140 uint32_t chunk_checksum;
142 int chunk_actually_sent;
148 static struct server_init_message server_info;
150 static void init_server_info() {
151 server_info.name_length = htonl(8);
152 memcpy(server_info.name_string, "NetWatch", 8);
155 static void update_server_info() {
157 outputf("RFB: setting fmt %d", fb->curmode.format);
158 server_info.fb_width = htons(fb->curmode.xres);
159 server_info.fb_height = htons(fb->curmode.yres);
160 switch (fb->curmode.format) {
162 server_info.fmt.bpp = 32;
163 server_info.fmt.depth = 24;
164 server_info.fmt.big_endian = 0;
165 server_info.fmt.true_color = 1;
166 server_info.fmt.red_max = htons(255);
167 server_info.fmt.green_max = htons(255);
168 server_info.fmt.blue_max = htons(255);
169 server_info.fmt.red_shift = 0;
170 server_info.fmt.green_shift = 8;
171 server_info.fmt.blue_shift = 16;
174 outputf("RFB: unknown fb fmt %d", fb->curmode.format);
178 outputf("RFB: fb null");
182 static int advance_chunk(struct rfb_state *state) {
184 state->chunk_xnum += 1;
186 if (state->chunk_xnum == SCREEN_CHUNKS_X) {
187 state->chunk_ynum += 1;
188 state->chunk_xnum = 0;
191 if (state->chunk_ynum == SCREEN_CHUNKS_Y) {
192 state->chunk_ynum = 0;
193 state->send_state = SST_IDLE;
194 if (!(state->chunk_actually_sent))
195 state->try_in_a_bit = 1;
202 static int ceildiv(int a, int b) {
210 static void send_fsm(struct tcp_pcb *pcb, struct rfb_state *state) {
211 struct update_header hdr;
218 switch (state->send_state) {
223 if (state->update_requested) {
224 outputf("RFB send: update requested");
225 state->update_requested = 0;
226 state->chunk_actually_sent = 0;
227 state->send_state = SST_HEADER;
232 /* FALL THROUGH to SST_HEADER */
236 /* Calculate the width and height for this chunk, remembering
237 * that if SCREEN_CHUNKS_[XY] do not evenly divide the width and
238 * height, we may need to have shorter chunks at the edge of
241 state->chunk_width = ceildiv(fb->curmode.xres, SCREEN_CHUNKS_X);
242 state->chunk_xpos = state->chunk_width * state->chunk_xnum;
243 totaldim = state->chunk_width * (state->chunk_xnum + 1);
244 if (totaldim > fb->curmode.xres) {
245 state->chunk_width -= (totaldim - fb->curmode.xres);
248 state->chunk_height = ceildiv(fb->curmode.yres, SCREEN_CHUNKS_Y);
249 state->chunk_ypos = state->chunk_height
251 totaldim = state->chunk_height * (state->chunk_ynum + 1);
252 if (totaldim > fb->curmode.yres) {
253 state->chunk_height -= (totaldim - fb->curmode.yres);
256 /* Do we _actually_ need to send this chunk? */
257 if (fb->checksum_rect) {
258 state->chunk_checksum = fb->checksum_rect(state->chunk_xpos, state->chunk_ypos,
259 state->chunk_width, state->chunk_height);
261 if (state->chunk_checksum == state->checksums[state->chunk_xnum][state->chunk_ynum]) {
262 if (advance_chunk(state))
266 /* Checksum gets set in data block, AFTER the data has been sent. */
269 state->chunk_actually_sent = 1;
273 state->chunk_bytes_sent = 0;
274 hdr.nrects = htons(1);
275 hdr.xpos = htons(state->chunk_xpos);
276 hdr.ypos = htons(state->chunk_ypos);
277 hdr.width = htons(state->chunk_width);
278 hdr.height= htons(state->chunk_height);
279 hdr.enctype = htonl(0);
281 err = tcp_write(pcb, &hdr, sizeof(hdr), TCP_WRITE_FLAG_COPY);
285 outputf("RFB: header send error %d", err);
287 /* Try again later. */
291 state->send_state = SST_DATA;
294 fb->copy_pixels(state->blockbuf,
295 state->chunk_xpos, state->chunk_ypos,
296 state->chunk_width, state->chunk_height);
298 /* FALL THROUGH to SST_DATA */
302 bytes_left = 4 * state->chunk_width * state->chunk_height - state->chunk_bytes_sent;
304 if (bytes_left == 0) {
305 state->send_state = SST_HEADER;
306 state->checksums[state->chunk_xnum][state->chunk_ynum] = state->chunk_checksum;
307 if (advance_chunk(state))
313 if (bytes_left > 1400) {
317 err = tcp_write(pcb, state->blockbuf + state->chunk_bytes_sent,
318 bytes_left, TCP_WRITE_FLAG_COPY);
321 state->chunk_bytes_sent += bytes_left;
324 outputf("RFB: send error %d", err);
329 if (tcp_sndbuf(pcb) == 0) {
335 if (tcp_output(pcb) != ERR_OK)
336 outputf("RFB: tcp_output bailed in send_fsm?");
339 static err_t rfb_sent(void *arg, struct tcp_pcb *pcb, uint16_t len) {
340 struct rfb_state *state = arg;
341 send_fsm(pcb, state);
345 static err_t rfb_poll(void *arg, struct tcp_pcb *pcb) {
346 struct rfb_state *state = arg;
347 if (state->try_in_a_bit) {
348 state->try_in_a_bit--;
349 if (!(state->try_in_a_bit)) {
350 state->update_requested = 1;
353 send_fsm(pcb, state);
360 static void close_conn(struct tcp_pcb *pcb, struct rfb_state *state) {
361 outputf("close_conn: bailing");
365 mem_free(state->blockbuf);
368 outputf("close_conn: done");
377 static enum fsm_result recv_fsm(struct tcp_pcb *pcb, struct rfb_state *state) {
381 outputf("RFB FSM: st %d rp %d wp %d", state->state, state->readpos,
384 switch(state->state) {
386 if (state->writepos < 12) return NEEDMORE;
388 if (!strncmp(state->data, "RFB 003.003\n", 12)) {
390 } else if (!strncmp(state->data, "RFB 003.005\n", 12)) {
391 /* Spec states that "RFB 003.005", an incorrect value,
392 * should be treated by the server as 3.3. */
394 } else if (!strncmp(state->data, "RFB 003.007\n", 12)) {
396 } else if (!strncmp(state->data, "RFB 003.008\n", 12)) {
399 outputf("RFB: Negotiation fail");
403 outputf("RFB: Negotiated v3.%d", state->version);
405 state->readpos += 12;
406 state->state = ST_CLIENTINIT;
408 /* We support one security type, currently "none".
409 * Send that and SecurityResult. */
410 if (state->version >= 7) {
411 tcp_write(pcb, "\x01\x01\x00\x00\x00\x00", 6, 0);
413 tcp_write(pcb, "\x01\x00\x00\x00\x00", 5, 0);
421 if (state->version >= 7) {
422 /* Ignore the security type and ClientInit */
423 if (state->writepos < 2) return NEEDMORE;
426 /* Just ClientInit */
427 if (state->writepos < 1) return NEEDMORE;
431 state->state = ST_MAIN;
433 outputf("RFB: Sending server info", state->version);
434 tcp_write(pcb, &server_info, sizeof(server_info), TCP_WRITE_FLAG_COPY);
440 if (state->writepos < 1) return NEEDMORE;
442 switch (state->data[0]) {
444 case SET_PIXEL_FORMAT:
446 if (state->writepos < (sizeof(struct pixel_format) + 4))
448 outputf("RFB: SetPixelFormat");
450 struct pixel_format * new_fmt =
451 (struct pixel_format *)(&state->data[4]);
455 state->readpos += sizeof(struct pixel_format) + 4;
459 if (state->writepos < 4) return NEEDMORE;
461 struct set_encs_req * req = (struct set_encs_req *)state->data;
463 pktsize = sizeof(struct set_encs_req) + (4 * ntohs(req->num));
465 outputf("RFB: SetEncodings [%d]", ntohs(req->num));
466 if (state->writepos < pktsize) return NEEDMORE;
468 for (i = 0; i < ntohs(req->num); i++) {
469 outputf("RFB: Encoding: %d", ntohl(req->encodings[i]));
473 state->readpos += pktsize;
476 case FB_UPDATE_REQUEST:
477 if (state->writepos < sizeof(struct fb_update_req))
479 outputf("RFB: UpdateRequest");
481 state->update_requested = 1;
482 memcpy(&state->client_interest_area, state->data,
483 sizeof(struct fb_update_req));
485 state->readpos += sizeof(struct fb_update_req);
489 if (state->writepos < sizeof(struct key_event_pkt))
492 struct key_event_pkt * p = (struct key_event_pkt *)state->data;
494 outputf("RFB: Key: %d (%c)", htonl(p->keysym), (htonl(p->keysym) & 0xFF));
495 kbd_inject_keysym(htonl(p->keysym), p->downflag);
497 state->readpos += sizeof(struct key_event_pkt);
501 if (state->writepos < sizeof(struct pointer_event_pkt))
503 outputf("RFB: Pointer");
507 state->readpos += sizeof(struct pointer_event_pkt);
510 case CLIENT_CUT_TEXT:
511 if (state->writepos < sizeof(struct text_event_pkt))
513 outputf("RFB: Cut Text");
515 struct text_event_pkt * pkt =
516 (struct text_event_pkt *)state->data;
518 if (state->writepos < sizeof(struct text_event_pkt)
524 state->readpos += sizeof(struct text_event_pkt)
529 outputf("RFB: Bad command: %d", state->data[0]);
533 outputf("RFB: Bad state");
538 static err_t rfb_recv(void *arg, struct tcp_pcb *pcb,
539 struct pbuf *p, err_t err) {
540 struct rfb_state *state = arg;
546 outputf("RFB: recv err %d", err);
547 /* FIXME do something better here? */
552 outputf("RFB: Connection closed");
553 close_conn(pcb, state);
557 if (p->tot_len > (RFB_BUF_SIZE - state->writepos)) {
559 outputf("RFB: Overflow!");
560 close_conn(pcb, state);
564 copylen = pbuf_copy_partial(p, state->data + state->writepos, p->tot_len, 0);
566 outputf("RFB: Processing %d, wp %d, cp %d", p->tot_len, state->writepos, copylen);
568 state->writepos += p->tot_len;
570 tcp_recved(pcb, p->tot_len);
574 switch (recv_fsm(pcb, state)) {
576 outputf("RFB FSM: blocking");
580 if (state->readpos == state->writepos) {
586 state->data + state->readpos,
587 state->writepos - state->readpos);
588 state->writepos -= state->readpos;
594 outputf("RFB: Protocol error");
595 close_conn(pcb, state);
602 /* Kick off a send. */
603 if (state->send_state == SST_IDLE && state->update_requested) {
604 send_fsm(pcb, state);
610 static err_t rfb_accept(void *arg, struct tcp_pcb *pcb, err_t err) {
611 struct rfb_state *state;
614 LWIP_UNUSED_ARG(arg);
615 LWIP_UNUSED_ARG(err);
617 state = (struct rfb_state *)mem_malloc(sizeof(struct rfb_state));
621 outputf("rfb_accept: out of memory\n");
625 memset(state, 0, sizeof(struct rfb_state));
627 blockbuf = mem_malloc(ceildiv(fb->curmode.xres, SCREEN_CHUNKS_X)
628 * ceildiv(fb->curmode.yres, SCREEN_CHUNKS_Y) * 4);
632 outputf("rfb_accept: out of memory allocating blockbuf\n");
637 state->blockbuf = blockbuf;
638 state->state = ST_BEGIN;
639 state->send_state = SST_IDLE;
641 /* XXX: update_server_info() should be called from the 64ms timer, and deal
642 * with screen resizes appropriately. */
643 update_server_info();
646 tcp_recv(pcb, rfb_recv);
647 tcp_sent(pcb, rfb_sent);
648 tcp_poll(pcb, rfb_poll, 1);
650 tcp_err(pcb, rfb_err);
652 tcp_write(pcb, "RFB 003.008\n", 12, 0);
658 static void rfb_init() {
664 tcp_bind(pcb, IP_ADDR_ANY, RFB_PORT);
665 pcb = tcp_listen(pcb);
666 tcp_accept(pcb, rfb_accept);