]> Joshua Wise's Git repositories - netwatch.git/blob - net/rfb.c
Another set of 3c90x style cleanups.
[netwatch.git] / net / rfb.c
1 #include <stdint.h>
2 #include <minilib.h>
3 #include <output.h>
4 #include <fb.h>
5
6 #include "lwip/tcp.h"
7
8 #include "rfb.h"
9
10 #define SET_PIXEL_FORMAT        0
11 #define SET_ENCODINGS           2
12 #define FB_UPDATE_REQUEST       3
13 #define KEY_EVENT               4
14 #define POINTER_EVENT           5
15 #define CLIENT_CUT_TEXT         6
16
17 #define RFB_BUF_SIZE    64
18
19 struct pixel_format {
20         uint8_t bpp;
21         uint8_t depth;
22         uint8_t big_endian;
23         uint8_t true_color;
24         uint16_t red_max;
25         uint16_t green_max;
26         uint16_t blue_max;
27         uint8_t red_shift;
28         uint8_t green_shift;
29         uint8_t blue_shift;
30         uint8_t padding[3];
31 };
32
33 struct server_init_message {
34         uint16_t fb_width;
35         uint16_t fb_height;
36         struct pixel_format fmt;
37         uint32_t name_length;
38         char name_string[8];
39 };
40
41 struct fb_update_req {
42         uint8_t msgtype;
43         uint8_t incremental;
44         uint16_t xpos;
45         uint16_t ypos;
46         uint16_t width;
47         uint16_t height;
48 };
49
50 struct set_encs_req {
51         uint8_t msgtype;
52         uint8_t padding;
53         uint16_t num;
54         int32_t encodings[];
55 };
56
57 struct key_event_pkt {
58         uint8_t msgtype;
59         uint8_t downflag;
60         uint8_t pad[2];
61         uint32_t keysym;
62 };
63
64 struct pointer_event_pkt {
65         uint8_t msgtype;
66         uint8_t button_mask;
67         uint16_t x;
68         uint16_t y;
69 };
70
71 struct text_event_pkt {
72         uint8_t msgtype;
73         uint8_t padding[3];
74         uint32_t length;
75         char text[];
76 };
77
78 struct update_header {
79         uint8_t msgtype;
80         uint8_t padding;
81         uint16_t nrects;
82         uint16_t xpos;
83         uint16_t ypos;
84         uint16_t width;
85         uint16_t height;
86         int32_t enctype;
87 };
88
89 struct rfb_state {
90         enum {
91                 ST_BEGIN,
92                 ST_CLIENTINIT,
93                 ST_MAIN
94         } state;
95         int version;
96         int encs_remaining;
97
98         char data[RFB_BUF_SIZE];
99         int readpos;
100         int writepos;
101
102         char next_update_incremental;
103         char update_requested;
104
105         struct fb_update_req client_interest_area;
106
107         enum {
108                 SST_IDLE,
109                 SST_NEEDS_UPDATE,
110                 SST_SENDING
111         } send_state;
112
113         uint32_t update_pos;
114         uint32_t frame_bytes;
115 };
116
117 static struct server_init_message server_info;
118
119 static void init_server_info() {
120         server_info.name_length = htonl(8);
121         memcpy(server_info.name_string, "NetWatch", 8);
122 }
123
124 static void update_server_info() {
125         if (fb != NULL) {
126                 outputf("RFB: setting fmt %d", fb->curmode.format);
127                 server_info.fb_width = htons(fb->curmode.xres);
128                 server_info.fb_height = htons(fb->curmode.yres);
129                 switch (fb->curmode.format) {
130                 case FB_RGB888:
131                         server_info.fmt.bpp = 32;
132                         server_info.fmt.depth = 24;
133                         server_info.fmt.big_endian = 0;
134                         server_info.fmt.true_color = 1;
135                         server_info.fmt.red_max = htons(255);
136                         server_info.fmt.green_max = htons(255);
137                         server_info.fmt.blue_max = htons(255);
138                         server_info.fmt.red_shift = 0;
139                         server_info.fmt.green_shift = 8;
140                         server_info.fmt.blue_shift = 16;
141                         break;
142                 default:
143                         outputf("RFB: unknown fb fmt %d", fb->curmode.format);
144                         break;
145                 }
146         } else {
147                 outputf("RFB: fb null");
148         }
149 }
150
151 static void send_fsm(struct tcp_pcb *pcb, struct rfb_state *state) {
152         struct update_header hdr;
153         int left, sndlength;
154         err_t err;
155
156         switch (state->send_state) {
157         case SST_IDLE:
158                 /* Nothing to do */
159                 if (state->update_requested) {
160                         outputf("RFB send: update requested");
161                         state->update_requested = 0;
162                         state->send_state = SST_NEEDS_UPDATE;
163                 } else {
164                         break;
165                 }
166         
167                 /* FALL THROUGH to SST_NEEDS_UPDATE*/
168         case SST_NEEDS_UPDATE:
169                 outputf("RFB send: sending header");
170                 /* Send a header */
171                 state->frame_bytes = fb->curmode.xres * fb->curmode.yres * fb->curmode.bytestride;
172                 hdr.msgtype = 0;
173                 hdr.nrects = htons(1);
174                 hdr.xpos = htons(0);
175                 hdr.ypos = htons(0);
176                 hdr.width = htons(fb->curmode.xres);
177                 hdr.height = htons(fb->curmode.yres);
178                 hdr.enctype = htonl(0);
179                 tcp_write(pcb, &hdr, sizeof(hdr), TCP_WRITE_FLAG_COPY);
180
181                 state->update_pos = 0;
182                 state->send_state = SST_SENDING;
183
184                 /* FALL THROUGH to SST_SENDING*/
185         case SST_SENDING:
186                 while (1) {
187                         left = state->frame_bytes - state->update_pos;
188
189                         if (left == 0) {
190                                 state->send_state = SST_IDLE;
191                                 break;
192                         }
193                         
194                         if (left > 8192)        /* Sounds good enough to me. */
195                                 left = 8192;
196
197                         sndlength = left;
198                         do {
199                                 err = tcp_write(pcb, fb->fbaddr + state->update_pos, sndlength, TCP_WRITE_FLAG_COPY /* The card can't DMA from there. */);
200                                 if (err == ERR_MEM)             /* Back down until lwip says we've got space. */
201                                         sndlength /= 2;
202                         } while (err == ERR_MEM && sndlength > 1);
203
204                         if (err != ERR_OK) {
205                                 if (err != ERR_MEM)
206                                         outputf("RFB: send error %d", err);
207                                 
208                                 /* We'll just give up for now and come back when we have space later. */
209                                 break;
210                         }
211
212                         state->update_pos += sndlength;
213
214                         if (tcp_sndbuf(pcb) == 0) {
215                                 break;
216                         }
217                 }
218
219                 break;
220         }
221         
222         if (tcp_output(pcb) != ERR_OK)
223                 outputf("RFB: tcp_output bailed in send_fsm?");
224 }
225
226 static err_t rfb_sent(void *arg, struct tcp_pcb *pcb, uint16_t len) {
227         struct rfb_state *state = arg;
228         send_fsm(pcb, state);
229         return ERR_OK;
230 }
231
232 static void close_conn(struct tcp_pcb *pcb, struct rfb_state *state) {
233         tcp_arg(pcb, NULL);
234         tcp_sent(pcb, NULL);
235         tcp_recv(pcb, NULL);
236         mem_free(state);
237         tcp_close(pcb);
238 }
239
240 enum fsm_result {
241         NEEDMORE,
242         OK,
243         FAIL
244 };
245
246 static enum fsm_result recv_fsm(struct tcp_pcb *pcb, struct rfb_state *state) {
247         int i;
248         int pktsize;
249
250         outputf("RFB FSM: st %d rp %d wp %d", state->state, state->readpos,
251                 state->writepos);
252
253         switch(state->state) {
254         case ST_BEGIN:
255                 if (state->writepos < 12) return NEEDMORE;
256
257                 if (!strncmp(state->data, "RFB 003.003\n", 12)) {
258                         state->version = 3;
259                 } else if (!strncmp(state->data, "RFB 003.005\n", 12)) {
260                         /* Spec states that "RFB 003.005", an incorrect value,
261                          * should be treated by the server as 3.3. */
262                         state->version = 3;
263                 } else if (!strncmp(state->data, "RFB 003.007\n", 12)) {
264                         state->version = 7;
265                 } else if (!strncmp(state->data, "RFB 003.008\n", 12)) {
266                         state->version = 8;
267                 } else {
268                         outputf("RFB: Negotiation fail");
269                         return FAIL;
270                 }
271
272                 outputf("RFB: Negotiated v3.%d", state->version);
273
274                 state->readpos += 12;
275                 state->state = ST_CLIENTINIT;
276
277                 /* We support one security type, currently "none".
278                  * Send that and SecurityResult. */
279                 if (state->version >= 7) {
280                         tcp_write(pcb, "\x01\x01\x00\x00\x00\x00", 6, 0);
281                 } else {
282                         tcp_write(pcb, "\x01\x00\x00\x00\x00", 5, 0);
283                 }
284
285                 tcp_output(pcb);
286
287                 return OK;
288
289         case ST_CLIENTINIT:
290                 if (state->version >= 7) {
291                         /* Ignore the security type and ClientInit */
292                         if (state->writepos < 2) return NEEDMORE;
293                         state->readpos += 2;
294                 } else {
295                         /* Just ClientInit */
296                         if (state->writepos < 1) return NEEDMORE;
297                         state->readpos += 1;
298                 }
299
300                 state->state = ST_MAIN;
301
302                 outputf("RFB: Sending server info", state->version);
303                 tcp_write(pcb, &server_info, sizeof(server_info), TCP_WRITE_FLAG_COPY);
304                 tcp_output(pcb);
305
306                 return OK;
307
308         case ST_MAIN:
309                 if (state->writepos < 1) return NEEDMORE;
310
311                 outputf("RFB: cmd %d", state->data[0]);
312                 switch (state->data[0]) {
313
314                 case SET_PIXEL_FORMAT:
315                         /* SetPixelFormat */
316                         if (state->writepos < (sizeof(struct pixel_format) + 4))
317                                 return NEEDMORE;
318                         outputf("RFB: SetPixelFormat");
319 /*
320                         struct pixel_format * new_fmt =
321                                 (struct pixel_format *)(&state->data[4]);
322 */
323                         /* XXX ... */
324
325                         state->readpos += sizeof(struct pixel_format) + 4;
326                         return OK;
327
328                 case SET_ENCODINGS:
329                         if (state->writepos < 4) return NEEDMORE;
330
331                         struct set_encs_req * req = (struct set_encs_req *)state->data;
332
333                         pktsize = sizeof(struct set_encs_req) + (4 * ntohs(req->num));
334
335                         outputf("RFB: SetEncodings [%d]", ntohs(req->num));
336                         if (state->writepos < pktsize) return NEEDMORE;
337
338                         for (i = 0; i < ntohs(req->num); i++) {
339                                 outputf("RFB: Encoding: %d", ntohl(req->encodings[i]));
340                                 /* XXX ... */
341                         }
342
343                         state->readpos += pktsize;
344                         return OK;
345
346                 case FB_UPDATE_REQUEST:
347                         if (state->writepos < sizeof(struct fb_update_req))
348                                 return NEEDMORE;
349                         outputf("RFB: UpdateRequest");
350
351                         state->update_requested = 1;
352                         memcpy(&state->client_interest_area, state->data,
353                                sizeof(struct fb_update_req)); 
354
355                         state->readpos += sizeof(struct fb_update_req);
356                         return OK;
357
358                 case KEY_EVENT:
359                         if (state->writepos < sizeof(struct key_event_pkt))
360                                 return NEEDMORE;
361                         outputf("RFB: Key");
362
363                         /* XXX stub */
364
365                         state->readpos += sizeof(struct key_event_pkt);
366                         return OK;
367
368                 case POINTER_EVENT:
369                         if (state->writepos < sizeof(struct pointer_event_pkt))
370                                 return NEEDMORE;
371                         outputf("RFB: Pointer");
372
373                         /* XXX stub */
374
375                         state->readpos += sizeof(struct pointer_event_pkt);
376                         return OK;
377
378                 case CLIENT_CUT_TEXT:
379                         if (state->writepos < sizeof(struct text_event_pkt))
380                                 return NEEDMORE;
381                         outputf("RFB: Cut Text");
382
383                         struct text_event_pkt * pkt =
384                                 (struct text_event_pkt *)state->data;
385
386                         if (state->writepos < sizeof(struct text_event_pkt)
387                                               + pkt->length)
388                                 return NEEDMORE;
389
390                         /* XXX stub */
391
392                         state->readpos += sizeof(struct text_event_pkt)
393                                           + pkt->length;
394                         return OK;
395
396                 default:
397                         outputf("RFB: Bad command: %d", state->data[0]);
398                 }
399         default:
400                 outputf("RFB: Bad state");
401                 return FAIL;
402         }
403 }
404
405 static err_t rfb_recv(void *arg, struct tcp_pcb *pcb,
406                       struct pbuf *p, err_t err) {
407         struct rfb_state *state = arg;
408
409         if (state == NULL) 
410
411         if (err != ERR_OK) {
412                 outputf("RFB: recv err %d", err);
413                 /* FIXME do something better here? */
414                 return ERR_OK;
415         }
416
417         if (p == NULL) {
418                 outputf("RFB: Connection closed");
419                 close_conn(pcb, state);
420                 return ERR_OK;
421         }
422
423         if (p->tot_len > (RFB_BUF_SIZE - state->writepos)) {
424                 /* Overflow! */
425                 outputf("RFB: Overflow!");
426                 close_conn(pcb, state);
427                 return ERR_OK;
428         }
429
430         outputf("RFB: Processing %d", p->tot_len);
431         pbuf_copy_partial(p, state->data + state->writepos, p->tot_len, 0);
432         state->writepos += p->tot_len;
433
434         tcp_recved(pcb, p->tot_len);
435         pbuf_free(p);
436
437         while (1) {
438                 switch (recv_fsm(pcb, state)) {
439                 case NEEDMORE:
440                         outputf("RFB FSM: blocking");
441                         /* Need more data */
442                         return ERR_OK;
443
444                 case OK:
445                         outputf("RFB FSM: ok");
446
447                         /* Kick off a send. */
448                         if (state->send_state == SST_IDLE
449                             && state->update_requested) {
450                                 send_fsm(pcb, state);
451                         }
452
453                         if (state->readpos == state->writepos) {
454                                 state->readpos = 0;
455                                 state->writepos = 0;
456                                 return ERR_OK;
457                         } else {
458                                 memmove(state->data,
459                                         state->data + state->readpos,
460                                         state->writepos - state->readpos);
461                         }
462                         break;
463                 case FAIL:
464                         /* Shit */
465                         outputf("RFB: Protocol error");
466                         close_conn(pcb, state);
467                         return ERR_OK;
468                 }
469         }
470 }       
471                 
472 static err_t rfb_accept(void *arg, struct tcp_pcb *pcb, err_t err) {
473         struct rfb_state *state;
474
475         LWIP_UNUSED_ARG(arg);
476         LWIP_UNUSED_ARG(err);
477
478         state = (struct rfb_state *)mem_malloc(sizeof(struct rfb_state));
479
480         state->state = ST_BEGIN;
481         state->readpos = 0;
482         state->writepos = 0;
483         state->update_requested = 0;
484         state->send_state = SST_IDLE;
485
486         /* XXX: update_server_info() should be called from the 64ms timer, and deal
487          * with screen resizes appropriately. */
488         update_server_info();
489
490         if (!state)
491         {
492                 outputf("rfb_accept: out of memory\n");
493                 return ERR_MEM;
494         }
495
496         tcp_arg(pcb, state);
497         tcp_recv(pcb, rfb_recv);
498         tcp_sent(pcb, rfb_sent);
499 /*
500         tcp_err(pcb, rfb_err);
501         tcp_poll(pcb, rfb_poll, 2);
502 */
503         tcp_write(pcb, "RFB 003.008\n", 12, 0);
504         tcp_output(pcb);
505
506         return ERR_OK;
507 }
508
509 void rfb_init() {
510         struct tcp_pcb *pcb;
511
512         init_server_info();
513
514         pcb = tcp_new();
515         tcp_bind(pcb, IP_ADDR_ANY, RFB_PORT);
516         pcb = tcp_listen(pcb);
517         tcp_accept(pcb, rfb_accept);
518 }
This page took 0.056101 seconds and 4 git commands to generate.