]> Joshua Wise's Git repositories - netwatch.git/blame_incremental - net/rfb.c
First pass of 410watch UI code.
[netwatch.git] / net / rfb.c
... / ...
CommitLineData
1/* rfb.c
2 * Remote framebuffer server
3 * NetWatch system management mode administration console
4 *
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.
8 *
9 */
10
11#include <stdint.h>
12#include <minilib.h>
13#include <output.h>
14#include <fb.h>
15#include <keyboard.h>
16#include <tables.h>
17
18#include "lwip/tcp.h"
19#include "lwip/stats.h"
20
21#define RFB_PORT 5900
22
23#define SET_PIXEL_FORMAT 0
24#define SET_ENCODINGS 2
25#define FB_UPDATE_REQUEST 3
26#define KEY_EVENT 4
27#define POINTER_EVENT 5
28#define CLIENT_CUT_TEXT 6
29
30#define RFB_BUF_SIZE 1536
31
32#define SCREEN_CHUNKS_X 8
33#define SCREEN_CHUNKS_Y 8
34
35struct pixel_format {
36 uint8_t bpp;
37 uint8_t depth;
38 uint8_t big_endian;
39 uint8_t true_color;
40 uint16_t red_max;
41 uint16_t green_max;
42 uint16_t blue_max;
43 uint8_t red_shift;
44 uint8_t green_shift;
45 uint8_t blue_shift;
46 uint8_t padding[3];
47};
48
49struct server_init_message {
50 uint16_t fb_width;
51 uint16_t fb_height;
52 struct pixel_format fmt;
53 uint32_t name_length;
54 char name_string[8];
55};
56
57struct fb_update_req {
58 uint8_t msgtype;
59 uint8_t incremental;
60 uint16_t xpos;
61 uint16_t ypos;
62 uint16_t width;
63 uint16_t height;
64};
65
66struct set_encs_req {
67 uint8_t msgtype;
68 uint8_t padding;
69 uint16_t num;
70 int32_t encodings[];
71};
72
73struct key_event_pkt {
74 uint8_t msgtype;
75 uint8_t downflag;
76 uint8_t pad[2];
77 uint32_t keysym;
78};
79
80struct pointer_event_pkt {
81 uint8_t msgtype;
82 uint8_t button_mask;
83 uint16_t x;
84 uint16_t y;
85};
86
87struct text_event_pkt {
88 uint8_t msgtype;
89 uint8_t padding[3];
90 uint32_t length;
91 char text[];
92};
93
94struct update_header {
95 uint8_t msgtype;
96 uint8_t padding;
97 uint16_t nrects;
98 uint16_t xpos;
99 uint16_t ypos;
100 uint16_t width;
101 uint16_t height;
102 int32_t enctype;
103};
104
105struct rfb_state {
106 enum {
107 ST_BEGIN = 0,
108 ST_CLIENTINIT,
109 ST_MAIN
110 } state;
111 int version;
112 int encs_remaining;
113
114 char data[RFB_BUF_SIZE];
115 int readpos;
116 int writepos;
117
118 char next_update_incremental;
119 char update_requested;
120
121 struct fb_update_req client_interest_area;
122
123 enum {
124 SST_IDLE = 0,
125 SST_HEADER,
126 SST_DATA
127 } send_state;
128
129 uint32_t checksums[SCREEN_CHUNKS_X][SCREEN_CHUNKS_Y];
130
131 uint32_t chunk_xnum;
132 uint32_t chunk_ynum;
133 uint32_t chunk_xpos;
134 uint32_t chunk_ypos;
135 uint32_t chunk_width;
136 uint32_t chunk_height;
137
138 uint32_t chunk_bytes_sent;
139
140 uint32_t chunk_checksum;
141
142 int chunk_actually_sent;
143 int try_in_a_bit;
144
145 char * blockbuf;
146};
147
148static struct server_init_message server_info;
149
150static void init_server_info() {
151 server_info.name_length = htonl(8);
152 memcpy(server_info.name_string, "NetWatch", 8);
153}
154
155static void update_server_info() {
156 if (fb != NULL) {
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) {
161 case FB_RGB888:
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;
172 break;
173 default:
174 outputf("RFB: unknown fb fmt %d", fb->curmode.format);
175 break;
176 }
177 } else {
178 outputf("RFB: fb null");
179 }
180}
181
182static int advance_chunk(struct rfb_state *state) {
183
184 state->chunk_xnum += 1;
185
186 if (state->chunk_xnum == SCREEN_CHUNKS_X) {
187 state->chunk_ynum += 1;
188 state->chunk_xnum = 0;
189 }
190
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;
196 return 1;
197 }
198
199 return 0;
200}
201
202static int ceildiv(int a, int b) {
203 int res = a / b;
204 if (a % b != 0) {
205 res++;
206 }
207 return res;
208}
209
210static void send_fsm(struct tcp_pcb *pcb, struct rfb_state *state) {
211 struct update_header hdr;
212 int bytes_left;
213 int totaldim;
214 err_t err;
215
216 while(1) {
217
218 switch (state->send_state) {
219
220 case SST_IDLE:
221 /* Nothing to do */
222
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;
228 } else {
229 return;
230 }
231
232 /* FALL THROUGH to SST_HEADER */
233
234 case SST_HEADER:
235
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
239 * the screen. */
240
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);
246 }
247
248 state->chunk_height = ceildiv(fb->curmode.yres, SCREEN_CHUNKS_Y);
249 state->chunk_ypos = state->chunk_height
250 * state->chunk_ynum;
251 totaldim = state->chunk_height * (state->chunk_ynum + 1);
252 if (totaldim > fb->curmode.yres) {
253 state->chunk_height -= (totaldim - fb->curmode.yres);
254 }
255
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);
260
261 if (state->chunk_checksum == state->checksums[state->chunk_xnum][state->chunk_ynum]) {
262 if (advance_chunk(state))
263 return;
264 continue;
265 }
266 /* Checksum gets set in data block, AFTER the data has been sent. */
267 }
268
269 state->chunk_actually_sent = 1;
270
271 /* Send a header */
272 hdr.msgtype = 0;
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);
280
281 err = tcp_write(pcb, &hdr, sizeof(hdr), TCP_WRITE_FLAG_COPY);
282
283 if (err != ERR_OK) {
284 if (err != ERR_MEM)
285 outputf("RFB: header send error %d", err);
286
287 /* Try again later. */
288 return;
289 }
290
291 state->send_state = SST_DATA;
292
293 /* Snag the data. */
294 fb->copy_pixels(state->blockbuf,
295 state->chunk_xpos, state->chunk_ypos,
296 state->chunk_width, state->chunk_height);
297
298 /* FALL THROUGH to SST_DATA */
299
300 case SST_DATA:
301
302 bytes_left = 4 * state->chunk_width * state->chunk_height - state->chunk_bytes_sent;
303
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))
308 return;
309 break;
310 }
311
312 /* That's enough. */
313 if (bytes_left > 1400) {
314 bytes_left = 1400;
315 }
316
317 err = tcp_write(pcb, state->blockbuf + state->chunk_bytes_sent,
318 bytes_left, TCP_WRITE_FLAG_COPY);
319
320 if (err == ERR_OK) {
321 state->chunk_bytes_sent += bytes_left;
322 } else {
323 if (err != ERR_MEM)
324 outputf("RFB: send error %d", err);
325
326 return;
327 }
328
329 if (tcp_sndbuf(pcb) == 0) {
330 return;
331 }
332 }
333 }
334
335 if (tcp_output(pcb) != ERR_OK)
336 outputf("RFB: tcp_output bailed in send_fsm?");
337}
338
339static err_t rfb_sent(void *arg, struct tcp_pcb *pcb, uint16_t len) {
340 struct rfb_state *state = arg;
341 send_fsm(pcb, state);
342 return ERR_OK;
343}
344
345static 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;
351 }
352 }
353 send_fsm(pcb, state);
354/*
355 stats_display();
356*/
357 return ERR_OK;
358}
359
360static void close_conn(struct tcp_pcb *pcb, struct rfb_state *state) {
361 outputf("close_conn: bailing");
362 tcp_arg(pcb, NULL);
363 tcp_sent(pcb, NULL);
364 tcp_recv(pcb, NULL);
365 mem_free(state->blockbuf);
366 mem_free(state);
367 tcp_close(pcb);
368 outputf("close_conn: done");
369}
370
371enum fsm_result {
372 NEEDMORE,
373 OK,
374 FAIL
375};
376
377static enum fsm_result recv_fsm(struct tcp_pcb *pcb, struct rfb_state *state) {
378 int i;
379 int pktsize;
380/*
381 outputf("RFB FSM: st %d rp %d wp %d", state->state, state->readpos,
382 state->writepos);
383*/
384 switch(state->state) {
385 case ST_BEGIN:
386 if (state->writepos < 12) return NEEDMORE;
387
388 if (!strncmp(state->data, "RFB 003.003\n", 12)) {
389 state->version = 3;
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. */
393 state->version = 3;
394 } else if (!strncmp(state->data, "RFB 003.007\n", 12)) {
395 state->version = 7;
396 } else if (!strncmp(state->data, "RFB 003.008\n", 12)) {
397 state->version = 8;
398 } else {
399 outputf("RFB: Negotiation fail");
400 return FAIL;
401 }
402
403 outputf("RFB: Negotiated v3.%d", state->version);
404
405 state->readpos += 12;
406 state->state = ST_CLIENTINIT;
407
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);
412 } else {
413 tcp_write(pcb, "\x01\x00\x00\x00\x00", 5, 0);
414 }
415
416 tcp_output(pcb);
417
418 return OK;
419
420 case ST_CLIENTINIT:
421 if (state->version >= 7) {
422 /* Ignore the security type and ClientInit */
423 if (state->writepos < 2) return NEEDMORE;
424 state->readpos += 2;
425 } else {
426 /* Just ClientInit */
427 if (state->writepos < 1) return NEEDMORE;
428 state->readpos += 1;
429 }
430
431 state->state = ST_MAIN;
432
433 outputf("RFB: Sending server info", state->version);
434 tcp_write(pcb, &server_info, sizeof(server_info), TCP_WRITE_FLAG_COPY);
435 tcp_output(pcb);
436
437 return OK;
438
439 case ST_MAIN:
440 if (state->writepos < 1) return NEEDMORE;
441
442 switch (state->data[0]) {
443
444 case SET_PIXEL_FORMAT:
445 /* SetPixelFormat */
446 if (state->writepos < (sizeof(struct pixel_format) + 4))
447 return NEEDMORE;
448 outputf("RFB: SetPixelFormat");
449/*
450 struct pixel_format * new_fmt =
451 (struct pixel_format *)(&state->data[4]);
452*/
453 /* XXX ... */
454
455 state->readpos += sizeof(struct pixel_format) + 4;
456 return OK;
457
458 case SET_ENCODINGS:
459 if (state->writepos < 4) return NEEDMORE;
460
461 struct set_encs_req * req = (struct set_encs_req *)state->data;
462
463 pktsize = sizeof(struct set_encs_req) + (4 * ntohs(req->num));
464
465 outputf("RFB: SetEncodings [%d]", ntohs(req->num));
466 if (state->writepos < pktsize) return NEEDMORE;
467
468 for (i = 0; i < ntohs(req->num); i++) {
469 outputf("RFB: Encoding: %d", ntohl(req->encodings[i]));
470 /* XXX ... */
471 }
472
473 state->readpos += pktsize;
474 return OK;
475
476 case FB_UPDATE_REQUEST:
477 if (state->writepos < sizeof(struct fb_update_req))
478 return NEEDMORE;
479 outputf("RFB: UpdateRequest");
480
481 state->update_requested = 1;
482 memcpy(&state->client_interest_area, state->data,
483 sizeof(struct fb_update_req));
484
485 state->readpos += sizeof(struct fb_update_req);
486 return OK;
487
488 case KEY_EVENT:
489 if (state->writepos < sizeof(struct key_event_pkt))
490 return NEEDMORE;
491
492 struct key_event_pkt * p = (struct key_event_pkt *)state->data;
493
494 outputf("RFB: Key: %d (%c)", htonl(p->keysym), (htonl(p->keysym) & 0xFF));
495 kbd_inject_keysym(htonl(p->keysym), p->downflag);
496
497 state->readpos += sizeof(struct key_event_pkt);
498 return OK;
499
500 case POINTER_EVENT:
501 if (state->writepos < sizeof(struct pointer_event_pkt))
502 return NEEDMORE;
503 outputf("RFB: Pointer");
504
505 /* XXX stub */
506
507 state->readpos += sizeof(struct pointer_event_pkt);
508 return OK;
509
510 case CLIENT_CUT_TEXT:
511 if (state->writepos < sizeof(struct text_event_pkt))
512 return NEEDMORE;
513 outputf("RFB: Cut Text");
514
515 struct text_event_pkt * pkt =
516 (struct text_event_pkt *)state->data;
517
518 if (state->writepos < sizeof(struct text_event_pkt)
519 + pkt->length)
520 return NEEDMORE;
521
522 /* XXX stub */
523
524 state->readpos += sizeof(struct text_event_pkt)
525 + pkt->length;
526 return OK;
527
528 default:
529 outputf("RFB: Bad command: %d", state->data[0]);
530 return FAIL;
531 }
532 default:
533 outputf("RFB: Bad state");
534 return FAIL;
535 }
536}
537
538static err_t rfb_recv(void *arg, struct tcp_pcb *pcb,
539 struct pbuf *p, err_t err) {
540 struct rfb_state *state = arg;
541 uint16_t copylen;
542
543 if (state == NULL)
544
545 if (err != ERR_OK) {
546 outputf("RFB: recv err %d", err);
547 /* FIXME do something better here? */
548 return ERR_OK;
549 }
550
551 if (p == NULL) {
552 outputf("RFB: Connection closed");
553 close_conn(pcb, state);
554 return ERR_OK;
555 }
556
557 if (p->tot_len > (RFB_BUF_SIZE - state->writepos)) {
558 /* Overflow! */
559 outputf("RFB: Overflow!");
560 close_conn(pcb, state);
561 return ERR_OK;
562 }
563
564 copylen = pbuf_copy_partial(p, state->data + state->writepos, p->tot_len, 0);
565
566 outputf("RFB: Processing %d, wp %d, cp %d", p->tot_len, state->writepos, copylen);
567
568 state->writepos += p->tot_len;
569
570 tcp_recved(pcb, p->tot_len);
571 pbuf_free(p);
572
573 while (1) {
574 switch (recv_fsm(pcb, state)) {
575 case NEEDMORE:
576 outputf("RFB FSM: blocking");
577 goto doneprocessing;
578
579 case OK:
580 if (state->readpos == state->writepos) {
581 state->readpos = 0;
582 state->writepos = 0;
583 goto doneprocessing;
584 } else {
585 memmove(state->data,
586 state->data + state->readpos,
587 state->writepos - state->readpos);
588 state->writepos -= state->readpos;
589 state->readpos = 0;
590 }
591 break;
592 case FAIL:
593 /* Shit */
594 outputf("RFB: Protocol error");
595 close_conn(pcb, state);
596 return ERR_OK;
597 }
598 }
599
600doneprocessing:
601
602 /* Kick off a send. */
603 if (state->send_state == SST_IDLE && state->update_requested) {
604 send_fsm(pcb, state);
605 }
606
607 return ERR_OK;
608}
609
610static err_t rfb_accept(void *arg, struct tcp_pcb *pcb, err_t err) {
611 struct rfb_state *state;
612 char * blockbuf;
613
614 LWIP_UNUSED_ARG(arg);
615 LWIP_UNUSED_ARG(err);
616
617 state = (struct rfb_state *)mem_malloc(sizeof(struct rfb_state));
618
619 if (!state)
620 {
621 outputf("rfb_accept: out of memory\n");
622 return ERR_MEM;
623 }
624
625 memset(state, 0, sizeof(struct rfb_state));
626
627 blockbuf = mem_malloc(ceildiv(fb->curmode.xres, SCREEN_CHUNKS_X)
628 * ceildiv(fb->curmode.yres, SCREEN_CHUNKS_Y) * 4);
629
630 if (!blockbuf)
631 {
632 outputf("rfb_accept: out of memory allocating blockbuf\n");
633 mem_free(state);
634 return ERR_MEM;
635 }
636
637 state->blockbuf = blockbuf;
638 state->state = ST_BEGIN;
639 state->send_state = SST_IDLE;
640
641 /* XXX: update_server_info() should be called from the 64ms timer, and deal
642 * with screen resizes appropriately. */
643 update_server_info();
644
645 tcp_arg(pcb, state);
646 tcp_recv(pcb, rfb_recv);
647 tcp_sent(pcb, rfb_sent);
648 tcp_poll(pcb, rfb_poll, 1);
649/*
650 tcp_err(pcb, rfb_err);
651*/
652 tcp_write(pcb, "RFB 003.008\n", 12, 0);
653 tcp_output(pcb);
654
655 return ERR_OK;
656}
657
658static void rfb_init() {
659 struct tcp_pcb *pcb;
660
661 init_server_info();
662
663 pcb = tcp_new();
664 tcp_bind(pcb, IP_ADDR_ANY, RFB_PORT);
665 pcb = tcp_listen(pcb);
666 tcp_accept(pcb, rfb_accept);
667}
668
669PROTOCOL(rfb_init);
This page took 0.033296 seconds and 4 git commands to generate.