size_t HardwareSerialX::write(uint8_t c) { // If the output buffer is full, there's nothing for it other than to // wait for the interrupt handler to empty it a bit // ???: return 0 here instead? while (ring_isfull( _tx_buffer )) ; ring_put( _tx_buffer, c ); sbi(*_ucsrb, _udrie); // clear the TXC bit -- "can be cleared by writing a one to its bit location" transmitting = true; sbi(*_ucsra, TXC0); return 1; }
int8_t GSwifi::parseHead2(uint8_t dat, int8_t cid) { if (dat == '\n') { continuous_newlines_ ++; } else if (dat == '\r') { // preserve } else { continuous_newlines_ = 0; if ( ! ring_isfull(_buf_cmd) ) { // ignore if overflowed ring_put( _buf_cmd, dat ); } } if (continuous_newlines_ == 1) { // check "Content-Length: x" header // if it's non 0, wait for more body data // otherwise disconnect after handling response // GS splits HTTP requests/responses into multiple bulk messages, // 1st ends just after headers, and 2nd contains only response body // "Content-Length: " .length = 16 // "Content-Length: 9999".length = 20 // "X-Requested-With: " .length = 18 char content_length_chars[21]; memset( content_length_chars, 0, 21 ); uint8_t copied = ring_get( _buf_cmd, content_length_chars, 20 ); if ((copied >= 16) && (strncmp(content_length_chars, "Content-Length: ", 16) == 0)) { content_length_chars[20] = 0; content_lengths_[ cid ] = atoi(&content_length_chars[16]); } if ((copied >= 18) && (strncmp(content_length_chars, "X-Requested-With: ", 18) == 0)) { has_requested_with_ = true; } ring_clear(_buf_cmd); } if (continuous_newlines_ == 2) { // if detected double (\r)\n, switch to body mode ring_clear(_buf_cmd); return 0; } return -1; }
static int8_t on_post_keys_response(int8_t cid, uint16_t status_code, GSwifi::GSREQUESTSTATE state) { HTTPLOG_PRINT(P("< P /k ")); HTTPLOG_PRINTLN(status_code); if (status_code != 200) { gs.bufferClear(); } if (state != GSwifi::GSREQUESTSTATE_RECEIVED) { return 0; } gs.writeHead( post_keys_cid, status_code ); switch (status_code) { case 200: while (! gs.bufferEmpty()) { char letter = gs.bufferGet(); gs.write( letter ); } gs.writeEnd(); break; default: gs.writeEnd(); break; } ring_put( &commands, COMMAND_CLOSE ); ring_put( &commands, cid ); ring_put( &commands, COMMAND_CLOSE ); if (ring_isfull( &commands )) { HTTPLOG_PRINTLN("!E8"); return -1; } ring_put( &commands, post_keys_cid ); return 0; }
int main() { ok( 1, "ok" ); { struct RingBuffer buf_; struct RingBuffer *buf = &buf_; char data[65]; ring_init( buf, data, 65 ); ok( ring_used(buf) == 0, "0 used" ); ok( ring_isempty(buf) == 1, "is empty" ); ring_put(buf, 'a'); ok( ring_used(buf) == 1, "1 used after put" ); ok( ring_isfull(buf) == 0, "not full" ); ok( ring_isempty(buf) == 0, "is empty" ); char buf2[64]; ring_get(buf, &buf2[0], 1); ok( buf2[0] == 'a', "get" ); ok( ring_used(buf) == 0, "0 used after get" ); ok( ring_isfull(buf) == 0, "not full" ); ok( ring_isempty(buf) == 1, "is empty" ); ring_put(buf, 'b'); ring_clear(buf); ok( ring_used(buf) == 0, "0 used after clear" ); ok( ring_isfull(buf) == 0, "not full" ); ok( ring_isempty(buf) == 1, "is empty" ); ring_put(buf, '0'); ring_put(buf, '1'); ring_put(buf, '2'); ring_put(buf, '3'); ring_put(buf, '4'); ring_put(buf, '5'); ring_put(buf, '6'); ring_put(buf, '7'); ring_put(buf, '8'); ring_put(buf, '9'); ring_put(buf, 'a'); ring_put(buf, 'b'); ring_put(buf, 'c'); ring_put(buf, 'd'); ring_put(buf, 'e'); ring_put(buf, 'f'); ring_put(buf, '0'); ring_put(buf, '1'); ring_put(buf, '2'); ring_put(buf, '3'); ring_put(buf, '4'); ring_put(buf, '5'); ring_put(buf, '6'); ring_put(buf, '7'); ring_put(buf, '8'); ring_put(buf, '9'); ring_put(buf, 'a'); ring_put(buf, 'b'); ring_put(buf, 'c'); ring_put(buf, 'd'); ring_put(buf, 'e'); ring_put(buf, 'f'); ring_put(buf, '0'); ring_put(buf, '1'); ring_put(buf, '2'); ring_put(buf, '3'); ring_put(buf, '4'); ring_put(buf, '5'); ring_put(buf, '6'); ring_put(buf, '7'); ring_put(buf, '8'); ring_put(buf, '9'); ring_put(buf, 'a'); ring_put(buf, 'b'); ring_put(buf, 'c'); ring_put(buf, 'd'); ring_put(buf, 'e'); ring_put(buf, 'f'); ring_put(buf, '0'); ring_put(buf, '1'); ring_put(buf, '2'); ring_put(buf, '3'); ring_put(buf, '4'); ring_put(buf, '5'); ring_put(buf, '6'); ring_put(buf, '7'); ring_put(buf, '8'); ring_put(buf, '9'); ring_put(buf, 'a'); ring_put(buf, 'b'); ring_put(buf, 'c'); ring_put(buf, 'd'); ring_put(buf, 'e'); ring_put(buf, 'f'); ok( ring_used(buf) == 64, "64 used after 64 puts" ); ok( ring_isfull(buf) == 1, "is full" ); ok( ring_isempty(buf) == 0, "is empty" ); // dropped feature to protect buffer from overflow // ok( ring_put(buf, 'x') == -1, "can't put into full" ); uint8_t fetched = ring_get(buf, &buf2[0], 64); ok( buf2[0] == '0', "get 1" ); ok( buf2[1] == '1', "get 2" ); ok( buf2[2] == '2', "get 3" ); ok( buf2[3] == '3', "get 4" ); ok( fetched == 64, "fetched all" ); ok( ring_used(buf) == 0, "0 used after all get" ); ok( ring_isfull(buf) == 0, "not full again" ); ok( ring_isempty(buf) == 1, "is empty" ); } done_testing(); }
int ring_ops(char *bfr,int bytes,enum ringbfract_t action,struct ringset *ring) { /* * Does ring buffer management. Actions can include * RING_ALLOC, RING_DEALLOC, RING_INIT, RING_STORE, RING_RETR, RING_ISMT, * RING_ISFULL, RING_ELEMENTS_FREE, RING_ELEMENTS_STORED,RING_VIEW, RING_DELETE * RING_ALLOC uses bytes to indicate the nuber of bytes to be stored in the ring. * The buffer will be allocated to one more than this * RING_DEALLOC deallocates the ring buffer * RING_STORE, RING_VIEW, and RING_RETR both use the *bfr and bytes. Others do not and * they can be set to zero. * RING_ISMT and RING_ISFULL, return 1 if true, 0 otherwise. * RING_RETR, RING_VIEW, and RING_STORE return -1 if the requested number of bytes is invalid * RING_ELEMENTS_FREE and RING_ELEMENTS_STORED return the number of bytes free and stored, repsectively. * * This should be called with the appropriate struct ringset *ring pointer to point to * data from the appropriate thread. */ int partial; switch (action) { case RING_ALLOC: // Allocate memory for the ring ring->ring = (char *)malloc(bytes+1); if(ring->ring == NULL) fail("ring_ops: Failed to allocate memory for ring buffer\n"); ring->bfrsize = bytes + 1; ring->readptr = ring->writeptr = 0; writelog(2,"ring_ops: Successfully allocated ring buffer to hold %d bytes\n",bytes); break; case RING_DEALLOC: // Free the buffer writelog(2,"ring_ops: De-allocating ring buffer of %d bytes\n",ring->bfrsize); free(ring->ring); break; case RING_INIT: // Initialize writelog(5,"ring_ops: Initializing ring buffer of %d bytes\n",ring->bfrsize); ring->readptr = ring->writeptr = 0; break; case RING_STORE: // Store bytes from bfr to ring if(bytes > ring_bytes_free(ring)) { return -1; } else { // Copy in two parts if it will wrap. partial = ring->bfrsize - ring->writeptr; if(bytes > partial) { memcpy(&ring->ring[ring->writeptr],bfr,partial); memcpy(ring->ring,&bfr[partial],bytes - partial); } else { memcpy(&ring->ring[ring->writeptr],bfr,bytes); } ring->writeptr = ring_normalize(ring->writeptr + bytes,ring); writelog(7,"ring_ops: Stored %d bytes into ring. new readptr: %d new writeptr: %d. " "Ring now contains %d bytes\n", bytes, ring->readptr,ring->writeptr, ring_bytes_stored(ring)); } break; case RING_RETR: // Retrieve bytes from the ring to bfr case RING_VIEW: // Retrieve but don't update the ring->readptr pointer if((bytes > 0) && (bytes > ring_bytes_stored(ring))) { return -1; } else { partial = ring->bfrsize - ring->readptr; if(bytes > partial) { // Copy in two parts if it will wrap memcpy(bfr,&(ring->ring[ring->readptr]),partial); memcpy(&bfr[partial],ring->ring,bytes - partial); } else { memcpy(bfr,&(ring->ring[ring->readptr]),bytes); } // Update the readptring pointer if action == retrieve if(action == RING_RETR) { ring->readptr = ring_normalize(ring->readptr + bytes,ring); writelog(7,"ring_ops: Retrieved %d bytes from ring. new readptr: %d new writeptr: %d. " "Ring now contains %d bytes\n", bytes, ring->readptr,ring->writeptr,ring_bytes_stored(ring)); } } break; case RING_DELETE: // Delete the given number of bytes from the readptr of the ring (update the ring->readptr). // If bytes is out of range, do nothing but return -1. if((bytes > 0) && (bytes <= ring_bytes_stored(ring))) { ring->readptr = ring_normalize(ring->readptr + bytes,ring); writelog(7,"ring_ops: Deleted %d bytes from ring. new readptr: %d new writeptr: %d. " "Ring now contains %d bytes\n", bytes, ring->readptr,ring->writeptr,ring_bytes_stored(ring)); } else { return -1; } break; case RING_ISMT: // Is empty? return ring_isempty(ring); break; case RING_ISFULL: // Is full? return ring_isfull(ring); break; case RING_ELEMENTS_FREE: // REturns the number of free bytes return ring_bytes_free(ring); break; case RING_ELEMENTS_STORED: // REturns the number of bytes currently stored. return ring_bytes_stored(ring); break; default: fail("ring_ops: Illegal action passed to ring_ops: %d\n",action); break; } return 0; }
// received a character from UART void GSwifi::parseByte(uint8_t dat) { static uint8_t next_token; // split each byte into tokens (cid,ip,port,length,data) static bool escape = false; char temp[GS_MAX_PATH_LENGTH+1]; if (dat == ESCAPE) { // 0x1B : Escape GSLOG_PRINT("e< "); } else { // if (next_token != NEXT_TOKEN_DATA) { GSLOG_WRITE(dat); } if (gs_mode_ == GSMODE_COMMAND) { if (escape) { // esc switch (dat) { case 'O': case 'F': // ignore break; case 'Z': case 'H': gs_mode_ = GSMODE_DATA_RX_BULK; next_token = NEXT_TOKEN_CID; break; default: // GSLOG_PRINT("!E1 "); GSLOG_PRINTLN2(dat,HEX); break; } escape = false; } else { if (dat == ESCAPE) { escape = true; } else if (dat == '\n') { // end of line parseLine(); } else if (dat != '\r') { if ( ! ring_isfull(_buf_cmd) ) { ring_put(_buf_cmd, dat); } else { GSLOG_PRINTLN("!E2"); } } } return; } else if (gs_mode_ != GSMODE_DATA_RX_BULK) { return; } static uint16_t len; static char len_chars[5]; static int8_t current_cid; static GSREQUESTSTATE request_state; if (next_token == NEXT_TOKEN_CID) { // dat is cid current_cid = x2i(dat); ASSERT((0 <= current_cid) && (current_cid <= 16)); next_token = NEXT_TOKEN_LENGTH; len = 0; } else if (next_token == NEXT_TOKEN_LENGTH) { // Data Length is 4 ascii char represents decimal value i.e. 1400 byte (0x31 0x34 0x30 0x30) len_chars[ len ++ ] = dat; if (len >= 4) { len_chars[ len ] = 0; len = atoi(len_chars); // length of data next_token = NEXT_TOKEN_DATA; if (content_lengths_[ current_cid ] > 0) { // this is our 2nd bulk message from GS for this response // we already swallowed HTTP response headers, // following should be body request_state = GSREQUESTSTATE_BODY; } else { request_state = GSREQUESTSTATE_HEAD1; } ring_clear( _buf_cmd ); // reuse _buf_cmd to store HTTP request } } else if (next_token == NEXT_TOKEN_DATA) { len --; if (cidIsRequest(current_cid)) { // request against us static uint16_t error_code; static int8_t routeid; switch (request_state) { case GSREQUESTSTATE_HEAD1: if (dat != '\n') { if ( ! ring_isfull(_buf_cmd) ) { ring_put( _buf_cmd, dat ); } // ignore overflowed } else { // end of request line // reuse "temp" buffer to parse method and path int8_t result = parseRequestLine((char*)temp, 7); GSMETHOD method = GSMETHOD_UNKNOWN; if ( result == 0 ) { method = x2method(temp); result = parseRequestLine((char*)temp, GS_MAX_PATH_LENGTH); } if ( result != 0 ) { // couldn't detect method or path request_state = GSREQUESTSTATE_ERROR; error_code = 400; ring_clear(_buf_cmd); break; } routeid = router(method, temp); if ( routeid < 0 ) { request_state = GSREQUESTSTATE_ERROR; error_code = 404; ring_clear(_buf_cmd); break; } request_state = GSREQUESTSTATE_HEAD2; continuous_newlines_ = 0; content_lengths_[ current_cid ] = 0; has_requested_with_ = false; ring_clear(_buf_cmd); } break; case GSREQUESTSTATE_HEAD2: if(0 == parseHead2(dat, current_cid)) { request_state = GSREQUESTSTATE_BODY; // dispatched once, at start of body dispatchRequestHandler(current_cid, routeid, GSREQUESTSTATE_BODY_START); } break; case GSREQUESTSTATE_BODY: if (content_lengths_[ current_cid ] > 0) { content_lengths_[ current_cid ] --; } if (ring_isfull(_buf_cmd)) { dispatchRequestHandler(current_cid, routeid, request_state); // POST, user callback should write() } ring_put(_buf_cmd, dat); break; case GSREQUESTSTATE_ERROR: // skip until received whole request break; case GSREQUESTSTATE_RECEIVED: default: break; } // end of bulk transfered data if (len == 0) { gs_mode_ = GSMODE_COMMAND; if ( request_state == GSREQUESTSTATE_ERROR ) { writeHead( current_cid, error_code ); writeEnd(); ring_put( &commands, COMMAND_CLOSE ); ring_put( &commands, current_cid ); } else { if (content_lengths_[ current_cid ] == 0) { // if Content-Length header was longer than <ESC>Z length, // we wait til next bulk transfer request_state = GSREQUESTSTATE_RECEIVED; } // user callback should write(), writeEnd() and close() dispatchRequestHandler(current_cid, routeid, request_state); } ring_clear(_buf_cmd); } } else { // is request from us static uint16_t status_code; switch (request_state) { case GSREQUESTSTATE_HEAD1: if (dat != '\n') { if ( ! ring_isfull(_buf_cmd) ) { // ignore if overflowed ring_put( _buf_cmd, dat ); } } else { uint8_t i=0; // skip 9 characters "HTTP/1.1 " while (i++ < 9) { ring_get( _buf_cmd, &temp[0], 1 ); } // copy 3 numbers representing status code into temp buffer temp[ 3 ] = 0; int8_t count = ring_get( _buf_cmd, temp, 3 ); if (count != 3) { // protocol error // we should receive something like: "200 OK", "401 Unauthorized" status_code = 999; request_state = GSREQUESTSTATE_ERROR; break; } status_code = atoi(temp); request_state = GSREQUESTSTATE_HEAD2; continuous_newlines_ = 0; content_lengths_[ current_cid ] = 0; ring_clear(_buf_cmd); } break; case GSREQUESTSTATE_HEAD2: if(0 == parseHead2(dat, current_cid)) { request_state = GSREQUESTSTATE_BODY; // dispatched once, at start of body dispatchResponseHandler(current_cid, status_code, GSREQUESTSTATE_BODY_START); } break; case GSREQUESTSTATE_BODY: if (content_lengths_[ current_cid ] > 0) { content_lengths_[ current_cid ] --; } if (ring_isfull(_buf_cmd)) { dispatchResponseHandler(current_cid, status_code, GSREQUESTSTATE_BODY); } ring_put(_buf_cmd, dat); break; case GSREQUESTSTATE_ERROR: case GSREQUESTSTATE_RECEIVED: default: break; } if (len == 0) { gs_mode_ = GSMODE_COMMAND; if ( request_state == GSREQUESTSTATE_ERROR ) { dispatchResponseHandler(current_cid, status_code, request_state); } else { if (content_lengths_[ current_cid ] == 0) { // if Content-Length header was longer than <ESC>Z length, // we wait til all response body received. // we need to close our clientRequest before handling it. // GS often locks when closing 2 connections in a row // ex: POST /keys from iPhone (cid:1) -> POST /keys to server (cid:2) // response from server arrives -> close(1) -> close(2) -> lock!! // the other way around: close(2) -> close(1) doesn't lock :( request_state = GSREQUESTSTATE_RECEIVED; } dispatchResponseHandler(current_cid, status_code, request_state); } ring_clear( _buf_cmd ); } } // is response } // (next_token == NEXT_TOKEN_DATA) }