]> Joshua Wise's Git repositories - netwatch.git/blame - net/rfb.c
Fix smm_type detection to actually detect, and add a state_num_regs routine.
[netwatch.git] / net / rfb.c
CommitLineData
3c4e084d
JP
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
7da36fbd
JP
11#include <stdint.h>
12#include <minilib.h>
13#include <output.h>
14#include <fb.h>
33e49b2e 15#include <keyboard.h>
4e3ef36b 16#include <tables.h>
ec949d20 17
7da36fbd 18#include "lwip/tcp.h"
b59a6e50 19#include "lwip/stats.h"
7da36fbd 20
4e3ef36b 21#define RFB_PORT 5900
7da36fbd
JP
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
e9654237 30#define RFB_BUF_SIZE 1536
7da36fbd 31
ec5f9ca5 32#define SCREEN_CHUNKS_X 8
b496c2b1
JP
33#define SCREEN_CHUNKS_Y 8
34
7da36fbd
JP
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
8ceb0515
JP
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
7da36fbd
JP
105struct rfb_state {
106 enum {
751f179d 107 ST_BEGIN = 0,
7da36fbd
JP
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;
8ceb0515
JP
119 char update_requested;
120
7da36fbd
JP
121 struct fb_update_req client_interest_area;
122
8ceb0515 123 enum {
751f179d 124 SST_IDLE = 0,
d9b82f8c
JP
125 SST_HEADER,
126 SST_DATA
8ceb0515
JP
127 } send_state;
128
99a4c846 129 uint32_t checksums[SCREEN_CHUNKS_X][SCREEN_CHUNKS_Y];
b496c2b1
JP
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
ec5f9ca5 138 uint32_t chunk_bytes_sent;
d136cf4c
JW
139
140 uint32_t chunk_checksum;
d9b82f8c 141
751f179d
JP
142 int chunk_actually_sent;
143 int try_in_a_bit;
ec5f9ca5
JP
144
145 char * blockbuf;
7da36fbd
JP
146};
147
148static struct server_init_message server_info;
149
150static void init_server_info() {
80726808 151 server_info.name_length = htonl(8);
7da36fbd
JP
152 memcpy(server_info.name_string, "NetWatch", 8);
153}
154
155static void update_server_info() {
156 if (fb != NULL) {
80726808
JP
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);
7da36fbd
JP
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;
80726808
JP
166 server_info.fmt.red_max = htons(255);
167 server_info.fmt.green_max = htons(255);
168 server_info.fmt.blue_max = htons(255);
7da36fbd
JP
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 }
80726808
JP
177 } else {
178 outputf("RFB: fb null");
7da36fbd
JP
179 }
180}
181
d9b82f8c
JP
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))
8ba85474 195 state->try_in_a_bit = 1;
d9b82f8c
JP
196 return 1;
197 }
198
199 return 0;
200}
201
ec5f9ca5
JP
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
8ceb0515
JP
210static void send_fsm(struct tcp_pcb *pcb, struct rfb_state *state) {
211 struct update_header hdr;
ec5f9ca5 212 int bytes_left;
b496c2b1 213 int totaldim;
8ceb0515
JP
214 err_t err;
215
d9b82f8c 216 while(1) {
b496c2b1 217
d9b82f8c 218 switch (state->send_state) {
b496c2b1 219
d9b82f8c
JP
220 case SST_IDLE:
221 /* Nothing to do */
8ceb0515 222
d9b82f8c
JP
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
ec5f9ca5 241 state->chunk_width = ceildiv(fb->curmode.xres, SCREEN_CHUNKS_X);
d9b82f8c
JP
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 }
b496c2b1 247
ec5f9ca5 248 state->chunk_height = ceildiv(fb->curmode.yres, SCREEN_CHUNKS_Y);
d9b82f8c
JP
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 }
8ceb0515 255
d9b82f8c
JP
256 /* Do we _actually_ need to send this chunk? */
257 if (fb->checksum_rect) {
d136cf4c
JW
258 state->chunk_checksum = fb->checksum_rect(state->chunk_xpos, state->chunk_ypos,
259 state->chunk_width, state->chunk_height);
d9b82f8c 260
d136cf4c 261 if (state->chunk_checksum == state->checksums[state->chunk_xnum][state->chunk_ynum]) {
d9b82f8c
JP
262 if (advance_chunk(state))
263 return;
264 continue;
d9b82f8c 265 }
d136cf4c 266 /* Checksum gets set in data block, AFTER the data has been sent. */
d9b82f8c 267 }
b496c2b1 268
d9b82f8c 269 state->chunk_actually_sent = 1;
b496c2b1 270
d9b82f8c
JP
271 /* Send a header */
272 hdr.msgtype = 0;
ec5f9ca5 273 state->chunk_bytes_sent = 0;
d9b82f8c
JP
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);
b496c2b1 280
d9b82f8c 281 err = tcp_write(pcb, &hdr, sizeof(hdr), TCP_WRITE_FLAG_COPY);
b496c2b1 282
d9b82f8c
JP
283 if (err != ERR_OK) {
284 if (err != ERR_MEM)
285 outputf("RFB: header send error %d", err);
b496c2b1 286
d9b82f8c
JP
287 /* Try again later. */
288 return;
289 }
b496c2b1 290
d9b82f8c 291 state->send_state = SST_DATA;
b496c2b1 292
ec5f9ca5
JP
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
d9b82f8c 298 /* FALL THROUGH to SST_DATA */
99a4c846 299
d9b82f8c 300 case SST_DATA:
8ceb0515 301
ec5f9ca5 302 bytes_left = 4 * state->chunk_width * state->chunk_height - state->chunk_bytes_sent;
b496c2b1 303
ec5f9ca5 304 if (bytes_left == 0) {
d9b82f8c 305 state->send_state = SST_HEADER;
d136cf4c 306 state->checksums[state->chunk_xnum][state->chunk_ynum] = state->chunk_checksum;
d9b82f8c
JP
307 if (advance_chunk(state))
308 return;
309 break;
310 }
b496c2b1 311
ec5f9ca5
JP
312 /* That's enough. */
313 if (bytes_left > 1400) {
314 bytes_left = 1400;
315 }
b496c2b1 316
ec5f9ca5
JP
317 err = tcp_write(pcb, state->blockbuf + state->chunk_bytes_sent,
318 bytes_left, TCP_WRITE_FLAG_COPY);
bbfab433 319
d9b82f8c 320 if (err == ERR_OK) {
ec5f9ca5 321 state->chunk_bytes_sent += bytes_left;
d9b82f8c 322 } else {
8cc40c4d
JW
323 if (err != ERR_MEM)
324 outputf("RFB: send error %d", err);
b496c2b1 325
d9b82f8c 326 return;
bbfab433 327 }
b496c2b1 328
bbfab433 329 if (tcp_sndbuf(pcb) == 0) {
d9b82f8c 330 return;
bbfab433 331 }
8ceb0515 332 }
8ceb0515 333 }
075bbc71
JW
334
335 if (tcp_output(pcb) != ERR_OK)
075bbc71 336 outputf("RFB: tcp_output bailed in send_fsm?");
8ceb0515
JP
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;
80726808
JP
343}
344
b59a6e50
JP
345static err_t rfb_poll(void *arg, struct tcp_pcb *pcb) {
346 struct rfb_state *state = arg;
751f179d
JP
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 }
8ba85474 353 send_fsm(pcb, state);
99a4c846 354/*
b59a6e50 355 stats_display();
99a4c846 356*/
b59a6e50
JP
357 return ERR_OK;
358}
359
7da36fbd 360static void close_conn(struct tcp_pcb *pcb, struct rfb_state *state) {
779b8ca8 361 outputf("close_conn: bailing");
7da36fbd
JP
362 tcp_arg(pcb, NULL);
363 tcp_sent(pcb, NULL);
364 tcp_recv(pcb, NULL);
ec5f9ca5 365 mem_free(state->blockbuf);
ce29e1cc 366 mem_free(state);
7da36fbd 367 tcp_close(pcb);
779b8ca8 368 outputf("close_conn: done");
7da36fbd
JP
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) {
80726808
JP
378 int i;
379 int pktsize;
056e0e18 380/*
80726808
JP
381 outputf("RFB FSM: st %d rp %d wp %d", state->state, state->readpos,
382 state->writepos);
056e0e18 383*/
7da36fbd
JP
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
80726808
JP
408 /* We support one security type, currently "none".
409 * Send that and SecurityResult. */
7da36fbd 410 if (state->version >= 7) {
80726808 411 tcp_write(pcb, "\x01\x01\x00\x00\x00\x00", 6, 0);
7da36fbd 412 } else {
80726808 413 tcp_write(pcb, "\x01\x00\x00\x00\x00", 5, 0);
7da36fbd
JP
414 }
415
7da36fbd
JP
416 tcp_output(pcb);
417
418 return OK;
419
420 case ST_CLIENTINIT:
80726808
JP
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
7da36fbd
JP
431 state->state = ST_MAIN;
432
80726808 433 outputf("RFB: Sending server info", state->version);
075bbc71 434 tcp_write(pcb, &server_info, sizeof(server_info), TCP_WRITE_FLAG_COPY);
7da36fbd
JP
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;
80726808 448 outputf("RFB: SetPixelFormat");
7da36fbd
JP
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
80726808 463 pktsize = sizeof(struct set_encs_req) + (4 * ntohs(req->num));
7da36fbd 464
80726808
JP
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 ... */
80726808
JP
471 }
472
473 state->readpos += pktsize;
7da36fbd
JP
474 return OK;
475
476 case FB_UPDATE_REQUEST:
477 if (state->writepos < sizeof(struct fb_update_req))
478 return NEEDMORE;
80726808 479 outputf("RFB: UpdateRequest");
7da36fbd 480
8ceb0515 481 state->update_requested = 1;
7da36fbd
JP
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
ec949d20 492 struct key_event_pkt * p = (struct key_event_pkt *)state->data;
dadcd4fc
JP
493
494 outputf("RFB: Key: %d (%c)", htonl(p->keysym), (htonl(p->keysym) & 0xFF));
ec949d20 495 kbd_inject_keysym(htonl(p->keysym), p->downflag);
7da36fbd
JP
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;
80726808 503 outputf("RFB: Pointer");
7da36fbd
JP
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;
80726808 513 outputf("RFB: Cut Text");
7da36fbd
JP
514
515 struct text_event_pkt * pkt =
516 (struct text_event_pkt *)state->data;
517
518 if (state->writepos < sizeof(struct text_event_pkt)
d9b82f8c 519 + pkt->length)
7da36fbd
JP
520 return NEEDMORE;
521
522 /* XXX stub */
523
524 state->readpos += sizeof(struct text_event_pkt)
d9b82f8c 525 + pkt->length;
7da36fbd
JP
526 return OK;
527
528 default:
529 outputf("RFB: Bad command: %d", state->data[0]);
779b8ca8 530 return FAIL;
7da36fbd
JP
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,
d9b82f8c 539 struct pbuf *p, err_t err) {
7da36fbd 540 struct rfb_state *state = arg;
779b8ca8 541 uint16_t copylen;
7da36fbd
JP
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
779b8ca8 564 copylen = pbuf_copy_partial(p, state->data + state->writepos, p->tot_len, 0);
8ba85474 565
779b8ca8 566 outputf("RFB: Processing %d, wp %d, cp %d", p->tot_len, state->writepos, copylen);
8ba85474 567
80726808
JP
568 state->writepos += p->tot_len;
569
7da36fbd
JP
570 tcp_recved(pcb, p->tot_len);
571 pbuf_free(p);
572
573 while (1) {
574 switch (recv_fsm(pcb, state)) {
575 case NEEDMORE:
80726808 576 outputf("RFB FSM: blocking");
779b8ca8 577 goto doneprocessing;
7da36fbd
JP
578
579 case OK:
580 if (state->readpos == state->writepos) {
581 state->readpos = 0;
582 state->writepos = 0;
779b8ca8 583 goto doneprocessing;
7da36fbd
JP
584 } else {
585 memmove(state->data,
d9b82f8c
JP
586 state->data + state->readpos,
587 state->writepos - state->readpos);
779b8ca8
JP
588 state->writepos -= state->readpos;
589 state->readpos = 0;
7da36fbd
JP
590 }
591 break;
592 case FAIL:
593 /* Shit */
594 outputf("RFB: Protocol error");
595 close_conn(pcb, state);
596 return ERR_OK;
597 }
598 }
779b8ca8
JP
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;
7da36fbd
JP
608}
609
610static err_t rfb_accept(void *arg, struct tcp_pcb *pcb, err_t err) {
611 struct rfb_state *state;
ec5f9ca5 612 char * blockbuf;
7da36fbd
JP
613
614 LWIP_UNUSED_ARG(arg);
615 LWIP_UNUSED_ARG(err);
616
617 state = (struct rfb_state *)mem_malloc(sizeof(struct rfb_state));
618
ec5f9ca5
JP
619 if (!state)
620 {
621 outputf("rfb_accept: out of memory\n");
622 return ERR_MEM;
623 }
624
751f179d
JP
625 memset(state, 0, sizeof(struct rfb_state));
626
ec5f9ca5
JP
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;
80726808 638 state->state = ST_BEGIN;
8ceb0515 639 state->send_state = SST_IDLE;
80726808 640
7da36fbd
JP
641 /* XXX: update_server_info() should be called from the 64ms timer, and deal
642 * with screen resizes appropriately. */
643 update_server_info();
644
7da36fbd
JP
645 tcp_arg(pcb, state);
646 tcp_recv(pcb, rfb_recv);
8ceb0515 647 tcp_sent(pcb, rfb_sent);
b59a6e50 648 tcp_poll(pcb, rfb_poll, 1);
7da36fbd
JP
649/*
650 tcp_err(pcb, rfb_err);
7da36fbd
JP
651*/
652 tcp_write(pcb, "RFB 003.008\n", 12, 0);
653 tcp_output(pcb);
654
655 return ERR_OK;
656}
657
4e3ef36b 658static void rfb_init() {
7da36fbd
JP
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}
4e3ef36b
JW
668
669PROTOCOL(rfb_init);
This page took 0.088025 seconds and 4 git commands to generate.