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