]> Joshua Wise's Git repositories - netwatch.git/blob - net/rfb.c
First pass of 410watch UI code.
[netwatch.git] / net / rfb.c
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
35 struct 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
49 struct 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
57 struct 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
66 struct set_encs_req {
67         uint8_t msgtype;
68         uint8_t padding;
69         uint16_t num;
70         int32_t encodings[];
71 };
72
73 struct key_event_pkt {
74         uint8_t msgtype;
75         uint8_t downflag;
76         uint8_t pad[2];
77         uint32_t keysym;
78 };
79
80 struct pointer_event_pkt {
81         uint8_t msgtype;
82         uint8_t button_mask;
83         uint16_t x;
84         uint16_t y;
85 };
86
87 struct text_event_pkt {
88         uint8_t msgtype;
89         uint8_t padding[3];
90         uint32_t length;
91         char text[];
92 };
93
94 struct 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
105 struct 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
148 static struct server_init_message server_info;
149
150 static void init_server_info() {
151         server_info.name_length = htonl(8);
152         memcpy(server_info.name_string, "NetWatch", 8);
153 }
154
155 static 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
182 static 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
202 static int ceildiv(int a, int b) {
203         int res = a / b;
204         if (a % b != 0) {
205                 res++;
206         }
207         return res;
208 }
209         
210 static 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
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);
342         return ERR_OK;
343 }
344
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;
351                 }
352         }
353         send_fsm(pcb, state);
354 /*
355         stats_display();
356 */
357         return ERR_OK;
358 }
359
360 static 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
371 enum fsm_result {
372         NEEDMORE,
373         OK,
374         FAIL
375 };
376
377 static 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
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;
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
600 doneprocessing:
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                 
610 static 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
658 static 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
669 PROTOCOL(rfb_init);
This page took 0.064109 seconds and 4 git commands to generate.