X-Git-Url: http://git.joshuawise.com/netwatch.git/blobdiff_plain/9eee8073b16fad5359206bb7d832293ce878dd16..a807be6ab277fe4bde9285b55c2ac637160132cb:/net/rfb.c diff --git a/net/rfb.c b/net/rfb.c index 13f6fb4..de81688 100644 --- a/net/rfb.c +++ b/net/rfb.c @@ -1,14 +1,24 @@ +/* rfb.c + * Remote framebuffer server + * NetWatch system management mode administration console + * + * Copyright (c) 2008 Jacob Potter and Joshua Wise. All rights reserved. + * This program is free software; you can redistribute and/or modify it under + * the terms found in the file LICENSE in the root of this source tree. + * + */ + #include #include #include #include - -#include "../aseg-paging/keyboard.h" +#include +#include #include "lwip/tcp.h" #include "lwip/stats.h" -#include "rfb.h" +#define RFB_PORT 5900 #define SET_PIXEL_FORMAT 0 #define SET_ENCODINGS 2 @@ -17,9 +27,9 @@ #define POINTER_EVENT 5 #define CLIENT_CUT_TEXT 6 -#define RFB_BUF_SIZE 512 +#define RFB_BUF_SIZE 1536 -#define SCREEN_CHUNKS_X 8 +#define SCREEN_CHUNKS_X 8 #define SCREEN_CHUNKS_Y 8 struct pixel_format { @@ -94,7 +104,7 @@ struct update_header { struct rfb_state { enum { - ST_BEGIN, + ST_BEGIN = 0, ST_CLIENTINIT, ST_MAIN } state; @@ -111,7 +121,7 @@ struct rfb_state { struct fb_update_req client_interest_area; enum { - SST_IDLE, + SST_IDLE = 0, SST_HEADER, SST_DATA } send_state; @@ -125,9 +135,14 @@ struct rfb_state { uint32_t chunk_width; uint32_t chunk_height; - uint32_t chunk_lindex; + uint32_t chunk_bytes_sent; + + uint32_t chunk_checksum; + + int chunk_actually_sent; + int try_in_a_bit; - uint32_t chunk_actually_sent; + char * blockbuf; }; static struct server_init_message server_info; @@ -177,18 +192,24 @@ static int advance_chunk(struct rfb_state *state) { state->chunk_ynum = 0; state->send_state = SST_IDLE; if (!(state->chunk_actually_sent)) - state->update_requested = 1; + state->try_in_a_bit = 1; return 1; } return 0; } +static int ceildiv(int a, int b) { + int res = a / b; + if (a % b != 0) { + res++; + } + return res; +} + static void send_fsm(struct tcp_pcb *pcb, struct rfb_state *state) { struct update_header hdr; - int lines_left; - unsigned char * lptr; - uint32_t checksum; + int bytes_left; int totaldim; err_t err; @@ -217,18 +238,14 @@ static void send_fsm(struct tcp_pcb *pcb, struct rfb_state *state) { * height, we may need to have shorter chunks at the edge of * the screen. */ - state->chunk_width = fb->curmode.xres / SCREEN_CHUNKS_X; - if (fb->curmode.xres % SCREEN_CHUNKS_X != 0) - state->chunk_width += 1; + state->chunk_width = ceildiv(fb->curmode.xres, SCREEN_CHUNKS_X); state->chunk_xpos = state->chunk_width * state->chunk_xnum; totaldim = state->chunk_width * (state->chunk_xnum + 1); if (totaldim > fb->curmode.xres) { state->chunk_width -= (totaldim - fb->curmode.xres); } - state->chunk_height = fb->curmode.yres / SCREEN_CHUNKS_Y; - if (fb->curmode.yres % SCREEN_CHUNKS_Y != 0) - state->chunk_height += 1; + state->chunk_height = ceildiv(fb->curmode.yres, SCREEN_CHUNKS_Y); state->chunk_ypos = state->chunk_height * state->chunk_ynum; totaldim = state->chunk_height * (state->chunk_ynum + 1); @@ -236,39 +253,30 @@ static void send_fsm(struct tcp_pcb *pcb, struct rfb_state *state) { state->chunk_height -= (totaldim - fb->curmode.yres); } - outputf("rfb send: (%d [%d], %d [%d]) %d x %d", - state->chunk_xnum, state->chunk_xpos, - state->chunk_ynum, state->chunk_ypos, - state->chunk_width, state->chunk_height); - /* Do we _actually_ need to send this chunk? */ if (fb->checksum_rect) { - checksum = fb->checksum_rect(state->chunk_xpos, state->chunk_ypos, - state->chunk_width, state->chunk_height); + state->chunk_checksum = fb->checksum_rect(state->chunk_xpos, state->chunk_ypos, + state->chunk_width, state->chunk_height); - if (checksum == state->checksums[state->chunk_xnum][state->chunk_ynum]) { - outputf("!!!!!!! SKIPPING: %08x", checksum); + if (state->chunk_checksum == state->checksums[state->chunk_xnum][state->chunk_ynum]) { if (advance_chunk(state)) return; continue; - } else { - state->checksums[state->chunk_xnum][state->chunk_ynum] = checksum; } + /* Checksum gets set in data block, AFTER the data has been sent. */ } - outputf("actually sent"); state->chunk_actually_sent = 1; /* Send a header */ hdr.msgtype = 0; - state->chunk_lindex = 0; + state->chunk_bytes_sent = 0; hdr.nrects = htons(1); hdr.xpos = htons(state->chunk_xpos); hdr.ypos = htons(state->chunk_ypos); hdr.width = htons(state->chunk_width); hdr.height= htons(state->chunk_height); hdr.enctype = htonl(0); - lines_left = state->chunk_height; err = tcp_write(pcb, &hdr, sizeof(hdr), TCP_WRITE_FLAG_COPY); @@ -282,31 +290,35 @@ static void send_fsm(struct tcp_pcb *pcb, struct rfb_state *state) { state->send_state = SST_DATA; + /* Snag the data. */ + fb->copy_pixels(state->blockbuf, + state->chunk_xpos, state->chunk_ypos, + state->chunk_width, state->chunk_height); + /* FALL THROUGH to SST_DATA */ case SST_DATA: - lines_left = state->chunk_height - state->chunk_lindex; + bytes_left = 4 * state->chunk_width * state->chunk_height - state->chunk_bytes_sent; - if (lines_left == 0) { + if (bytes_left == 0) { state->send_state = SST_HEADER; + state->checksums[state->chunk_xnum][state->chunk_ynum] = state->chunk_checksum; if (advance_chunk(state)) return; break; } - lptr = fb->fbaddr - + (fb->curmode.xres * fb->curmode.bytestride - * (state->chunk_ypos + state->chunk_lindex)) - + (state->chunk_xpos * fb->curmode.bytestride); + /* That's enough. */ + if (bytes_left > 1400) { + bytes_left = 1400; + } - /* The network card can't DMA from video RAM, - * so use TCP_WRITE_FLAG_COPY. */ - err = tcp_write(pcb, lptr, - fb->curmode.bytestride * state->chunk_width, TCP_WRITE_FLAG_COPY); + err = tcp_write(pcb, state->blockbuf + state->chunk_bytes_sent, + bytes_left, TCP_WRITE_FLAG_COPY); if (err == ERR_OK) { - state->chunk_lindex += 1; + state->chunk_bytes_sent += bytes_left; } else { if (err != ERR_MEM) outputf("RFB: send error %d", err); @@ -332,6 +344,12 @@ static err_t rfb_sent(void *arg, struct tcp_pcb *pcb, uint16_t len) { static err_t rfb_poll(void *arg, struct tcp_pcb *pcb) { struct rfb_state *state = arg; + if (state->try_in_a_bit) { + state->try_in_a_bit--; + if (!(state->try_in_a_bit)) { + state->update_requested = 1; + } + } send_fsm(pcb, state); /* stats_display(); @@ -340,11 +358,14 @@ static err_t rfb_poll(void *arg, struct tcp_pcb *pcb) { } static void close_conn(struct tcp_pcb *pcb, struct rfb_state *state) { + outputf("close_conn: bailing"); tcp_arg(pcb, NULL); tcp_sent(pcb, NULL); tcp_recv(pcb, NULL); + mem_free(state->blockbuf); mem_free(state); tcp_close(pcb); + outputf("close_conn: done"); } enum fsm_result { @@ -356,10 +377,10 @@ enum fsm_result { static enum fsm_result recv_fsm(struct tcp_pcb *pcb, struct rfb_state *state) { int i; int pktsize; - +/* outputf("RFB FSM: st %d rp %d wp %d", state->state, state->readpos, state->writepos); - +*/ switch(state->state) { case ST_BEGIN: if (state->writepos < 12) return NEEDMORE; @@ -418,7 +439,6 @@ static enum fsm_result recv_fsm(struct tcp_pcb *pcb, struct rfb_state *state) { case ST_MAIN: if (state->writepos < 1) return NEEDMORE; - outputf("RFB: cmd %d", state->data[0]); switch (state->data[0]) { case SET_PIXEL_FORMAT: @@ -507,6 +527,7 @@ static enum fsm_result recv_fsm(struct tcp_pcb *pcb, struct rfb_state *state) { default: outputf("RFB: Bad command: %d", state->data[0]); + return FAIL; } default: outputf("RFB: Bad state"); @@ -517,6 +538,7 @@ static enum fsm_result recv_fsm(struct tcp_pcb *pcb, struct rfb_state *state) { static err_t rfb_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { struct rfb_state *state = arg; + uint16_t copylen; if (state == NULL) @@ -539,8 +561,10 @@ static err_t rfb_recv(void *arg, struct tcp_pcb *pcb, return ERR_OK; } - outputf("RFB: Processing %d", p->tot_len); - pbuf_copy_partial(p, state->data + state->writepos, p->tot_len, 0); + copylen = pbuf_copy_partial(p, state->data + state->writepos, p->tot_len, 0); + + outputf("RFB: Processing %d, wp %d, cp %d", p->tot_len, state->writepos, copylen); + state->writepos += p->tot_len; tcp_recved(pcb, p->tot_len); @@ -550,26 +574,19 @@ static err_t rfb_recv(void *arg, struct tcp_pcb *pcb, switch (recv_fsm(pcb, state)) { case NEEDMORE: outputf("RFB FSM: blocking"); - /* Need more data */ - return ERR_OK; + goto doneprocessing; case OK: - outputf("RFB FSM: ok"); - - /* Kick off a send. */ - if (state->send_state == SST_IDLE - && state->update_requested) { - send_fsm(pcb, state); - } - if (state->readpos == state->writepos) { state->readpos = 0; state->writepos = 0; - return ERR_OK; + goto doneprocessing; } else { memmove(state->data, state->data + state->readpos, state->writepos - state->readpos); + state->writepos -= state->readpos; + state->readpos = 0; } break; case FAIL: @@ -579,35 +596,52 @@ static err_t rfb_recv(void *arg, struct tcp_pcb *pcb, return ERR_OK; } } + +doneprocessing: + + /* Kick off a send. */ + if (state->send_state == SST_IDLE && state->update_requested) { + send_fsm(pcb, state); + } + + return ERR_OK; } static err_t rfb_accept(void *arg, struct tcp_pcb *pcb, err_t err) { struct rfb_state *state; + char * blockbuf; LWIP_UNUSED_ARG(arg); LWIP_UNUSED_ARG(err); state = (struct rfb_state *)mem_malloc(sizeof(struct rfb_state)); + if (!state) + { + outputf("rfb_accept: out of memory\n"); + return ERR_MEM; + } + + memset(state, 0, sizeof(struct rfb_state)); + + blockbuf = mem_malloc(ceildiv(fb->curmode.xres, SCREEN_CHUNKS_X) + * ceildiv(fb->curmode.yres, SCREEN_CHUNKS_Y) * 4); + + if (!blockbuf) + { + outputf("rfb_accept: out of memory allocating blockbuf\n"); + mem_free(state); + return ERR_MEM; + } + + state->blockbuf = blockbuf; state->state = ST_BEGIN; - state->readpos = 0; - state->writepos = 0; - state->update_requested = 0; state->send_state = SST_IDLE; - state->chunk_xnum = 0; - state->chunk_ynum = 0; - memset(state->checksums, 0, sizeof(state->checksums)); /* XXX: update_server_info() should be called from the 64ms timer, and deal * with screen resizes appropriately. */ update_server_info(); - if (!state) - { - outputf("rfb_accept: out of memory\n"); - return ERR_MEM; - } - tcp_arg(pcb, state); tcp_recv(pcb, rfb_recv); tcp_sent(pcb, rfb_sent); @@ -621,7 +655,7 @@ static err_t rfb_accept(void *arg, struct tcp_pcb *pcb, err_t err) { return ERR_OK; } -void rfb_init() { +static void rfb_init() { struct tcp_pcb *pcb; init_server_info(); @@ -631,3 +665,5 @@ void rfb_init() { pcb = tcp_listen(pcb); tcp_accept(pcb, rfb_accept); } + +PROTOCOL(rfb_init);