reorganization; remove json

This commit is contained in:
Peter McGoron 2022-12-31 20:15:24 +00:00
parent 6604e35b89
commit f8bf634345
6 changed files with 37 additions and 825 deletions

View File

@ -1,24 +0,0 @@
src/jsmn.h
===================
MIT License
Copyright (c) 2010 Serge Zaitsev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -3,15 +3,6 @@
#include <zephyr/net/socket.h>
#include "buf.h"
/* Read from the socket into the buffer.
* This function is meant to be called multiple times on the
* same struct. The controller loads bp->left with the amount
* of bytes it wishes to read, and continues until bp->left == 0.
*
* This function returns false if there was an error reading
* from the socket.
* A read of 0 bytes returns true.
*/
bool
buf_read_sock(int sock, struct bufptr *bp)
{
@ -24,12 +15,6 @@ buf_read_sock(int sock, struct bufptr *bp)
return true;
}
/* Write from the bufptr into a socket.
* This function is meant to be called once per prepared bufptr.
*
* This function returns false if there was an error on the
* socket.
*/
bool
buf_write_sock(int sock, struct bufptr *bp)
{
@ -44,21 +29,6 @@ buf_write_sock(int sock, struct bufptr *bp)
return true;
}
/* Write a formatted string to bp.
* This function uses printf(), which means that it deals with
* writing _C-strings_, not unterminated buffers.
* When using this function, the buffer must be _one more_ than
* the maximum message length. For instance, a 1024-byte message
* should be in a 1025-byte buffer. HOWEVER, bp->left must still
* be set to the total length of the buffer (in the example, 1025).
*
* The final bufptr points to the NUL terminator, so that it
* is overwritten on each call to the function.
*
* This function returns 0 for a successful write, -1 for an
* encoding error (should never happen), and a positive value
* for the amount of bytes that could not fit.
*/
int
buf_writevf(struct bufptr *bp, const char *fmt, va_list va)
{
@ -90,8 +60,10 @@ buf_writevf(struct bufptr *bp, const char *fmt, va_list va)
int
buf_writef(struct bufptr *bp, const char *fmt, ...)
{
int r;
va_list va;
va_start(va, fmt);
buf_writevf(bp, fmt, va);
r = buf_writevf(bp, fmt, va);
va_end(va);
return r;
}

View File

@ -1,8 +1,7 @@
#pragma once
/* This is a pointer _into_ a buffer. It is increment
* (and the variable left decremented) after each
* operation.
/* This is a pointer _into_ a buffer. It is incremented (and the variable
* "left" decremented) after each operation.
*/
struct bufptr {
char *p;
@ -14,7 +13,39 @@ enum {
BUF_WRITE_ERR = -1
};
/* Read from the socket into the buffer.
* This function is meant to be called multiple times on the
* same struct. The controller loads bp->left with the amount
* of bytes it wishes to read, and continues until bp->left == 0.
*
* This function returns false if there was an error reading
* from the socket.
* A read of 0 bytes returns true.
*/
bool buf_read_sock(int sock, struct bufptr *bp);
/* Write from the bufptr into a socket.
* This function is meant to be called once per prepared bufptr.
*
* This function returns false if there was an error on the
* socket.
*/
bool buf_write_sock(int sock, struct bufptr *bp);
/* Write a formatted string to bp.
* This function uses printf(), which means that it deals with
* writing _C-strings_, not unterminated buffers.
* When using this function, the buffer must be _one more_ than
* the maximum message length. For instance, a 1024-byte message
* should be in a 1025-byte buffer. HOWEVER, bp->left must still
* be set to the total length of the buffer (in the example, 1025).
*
* The final bufptr points to the NUL terminator, so that it
* is overwritten on each call to the function.
*
* This function returns 0 for a successful write, -1 for an
* encoding error (should never happen), and a positive value
* for the amount of bytes that could not fit.
*/
int buf_writevf(struct bufptr *bp, const char *fmt, va_list va);
int buf_writef(struct bufptr *bp, const char *fmt, ...);

View File

@ -1,502 +0,0 @@
/*
* Jsmn is JSON parser that does not use the C standard library and
* does not allocate memory. Jsmn stores the structure of the JSON
* document in an array that records
* * the type of the token,
* * the start position and extent of the token,
* * and the number of elements inside the token.
* For example, the document
{"key":"val", "obj" : { "x" : 2, "y" : false }, "arr" : [1, "2", 3]}
* would contain parse to:
* OBJECT 6, STRING "key", STRING "val", STRING "obj", OBJECT 4,
* STRING "key", PRIMITIVE 2, STRING y, PRIMITIVE false,
* STRING "arr", ARRAY 3, PRIMITIVE 1, STRING "2", PRIMITIVE 3
* The number next to OBJECT and ARRAY denote the size of each.
* Strings always start with a quote and end with one: primitives
* (numbers, booleans, null) do not.
*
* TOKENS ARE NOT NUL TERMINATED. However, because of how the JSON
* grammar works, the NUL terminator can be manually added.
* STRING ESCAPE SEQUENCES ARE NOT PARSED.
* Strings start at one-past the quotation mark, and the end index
* points to the end quotation mark.
*
* Why JSON?
* 1) Every language has a JSON library.
* 2) JSMN is perfectly suited to this program.
* 3) Another standard format would most likely require writing
* a parser by hand.
* 4) A minimalistic, custom format would require more debugging.
*
* From: https://github.com/zserge/jsmn 25647e692c7906b96ffd2b05ca54c097948e879c
*
* MIT License
*
* Copyright (c) 2010 Serge Zaitsev
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef JSMN_H
#define JSMN_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef JSMN_STATIC
#define JSMN_API static
#else
#define JSMN_API extern
#endif
/**
* JSON type identifier. Basic types are:
* o Object
* o Array
* o String
* o Other primitive: number, boolean (true/false) or null
*/
typedef enum {
JSMN_UNDEFINED = 0,
JSMN_OBJECT = 1 << 0,
JSMN_ARRAY = 1 << 1,
JSMN_STRING = 1 << 2,
JSMN_PRIMITIVE = 1 << 3
} jsmntype_t;
enum jsmnerr {
/* Not enough tokens were provided */
JSMN_ERROR_NOMEM = -1,
/* Invalid character inside JSON string */
JSMN_ERROR_INVAL = -2,
/* The string is not a full JSON packet, more bytes expected */
JSMN_ERROR_PART = -3
};
/**
* JSON token description.
* type type (object, array, string etc.)
* start start position in JSON data string
* end end position in JSON data string
*/
typedef struct jsmntok {
jsmntype_t type;
int start;
int end;
int size;
#ifdef JSMN_PARENT_LINKS
int parent;
#endif
} jsmntok_t;
/**
* JSON parser. Contains an array of token blocks available. Also stores
* the string being parsed now and current position in that string.
*/
typedef struct jsmn_parser {
unsigned int pos; /* offset in the JSON string */
unsigned int toknext; /* next token to allocate */
int toksuper; /* superior token node, e.g. parent object or array */
} jsmn_parser;
/**
* Create JSON parser over an array of tokens
*/
JSMN_API void jsmn_init(jsmn_parser *parser);
/**
* Run JSON parser. It parses a JSON data string into and array of tokens, each
* describing
* a single JSON object.
*/
JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
jsmntok_t *tokens, const unsigned int num_tokens);
#ifndef JSMN_HEADER
/**
* Allocates a fresh unused token from the token pool.
*/
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,
const size_t num_tokens) {
jsmntok_t *tok;
if (parser->toknext >= num_tokens) {
return NULL;
}
tok = &tokens[parser->toknext++];
tok->start = tok->end = -1;
tok->size = 0;
#ifdef JSMN_PARENT_LINKS
tok->parent = -1;
#endif
return tok;
}
/**
* Fills token type and boundaries.
*/
static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
const int start, const int end) {
token->type = type;
token->start = start;
token->end = end;
token->size = 0;
}
/**
* Fills next available token with JSON primitive.
*/
static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
const size_t len, jsmntok_t *tokens,
const size_t num_tokens) {
jsmntok_t *token;
int start;
start = parser->pos;
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
switch (js[parser->pos]) {
#ifndef JSMN_STRICT
/* In strict mode primitive must be followed by "," or "}" or "]" */
case ':':
#endif
case '\t':
case '\r':
case '\n':
case ' ':
case ',':
case ']':
case '}':
goto found;
default:
/* to quiet a warning from gcc*/
break;
}
if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
parser->pos = start;
return JSMN_ERROR_INVAL;
}
}
#ifdef JSMN_STRICT
/* In strict mode primitive must be followed by a comma/object/array */
parser->pos = start;
return JSMN_ERROR_PART;
#endif
found:
if (tokens == NULL) {
parser->pos--;
return 0;
}
token = jsmn_alloc_token(parser, tokens, num_tokens);
if (token == NULL) {
parser->pos = start;
return JSMN_ERROR_NOMEM;
}
jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
#ifdef JSMN_PARENT_LINKS
token->parent = parser->toksuper;
#endif
parser->pos--;
return 0;
}
/**
* Fills next token with JSON string.
*/
static int jsmn_parse_string(jsmn_parser *parser, const char *js,
const size_t len, jsmntok_t *tokens,
const size_t num_tokens) {
jsmntok_t *token;
int start = parser->pos;
/* Skip starting quote */
parser->pos++;
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
char c = js[parser->pos];
/* Quote: end of string */
if (c == '\"') {
if (tokens == NULL) {
return 0;
}
token = jsmn_alloc_token(parser, tokens, num_tokens);
if (token == NULL) {
parser->pos = start;
return JSMN_ERROR_NOMEM;
}
jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
#ifdef JSMN_PARENT_LINKS
token->parent = parser->toksuper;
#endif
return 0;
}
/* Backslash: Quoted symbol expected */
if (c == '\\' && parser->pos + 1 < len) {
int i;
parser->pos++;
switch (js[parser->pos]) {
/* Allowed escaped symbols */
case '\"':
case '/':
case '\\':
case 'b':
case 'f':
case 'r':
case 'n':
case 't':
break;
/* Allows escaped symbol \uXXXX */
case 'u':
parser->pos++;
for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0';
i++) {
/* If it isn't a hex character we have an error */
if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
(js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
parser->pos = start;
return JSMN_ERROR_INVAL;
}
parser->pos++;
}
parser->pos--;
break;
/* Unexpected symbol */
default:
parser->pos = start;
return JSMN_ERROR_INVAL;
}
}
}
parser->pos = start;
return JSMN_ERROR_PART;
}
/**
* Parse JSON string and fill tokens.
*/
JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
jsmntok_t *tokens, const unsigned int num_tokens) {
int r;
int i;
jsmntok_t *token;
int count = parser->toknext;
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
char c;
jsmntype_t type;
c = js[parser->pos];
switch (c) {
case '{':
case '[':
count++;
if (tokens == NULL) {
break;
}
token = jsmn_alloc_token(parser, tokens, num_tokens);
if (token == NULL) {
return JSMN_ERROR_NOMEM;
}
if (parser->toksuper != -1) {
jsmntok_t *t = &tokens[parser->toksuper];
#ifdef JSMN_STRICT
/* In strict mode an object or array can't become a key */
if (t->type == JSMN_OBJECT) {
return JSMN_ERROR_INVAL;
}
#endif
t->size++;
#ifdef JSMN_PARENT_LINKS
token->parent = parser->toksuper;
#endif
}
token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
token->start = parser->pos;
parser->toksuper = parser->toknext - 1;
break;
case '}':
case ']':
if (tokens == NULL) {
break;
}
type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
#ifdef JSMN_PARENT_LINKS
if (parser->toknext < 1) {
return JSMN_ERROR_INVAL;
}
token = &tokens[parser->toknext - 1];
for (;;) {
if (token->start != -1 && token->end == -1) {
if (token->type != type) {
return JSMN_ERROR_INVAL;
}
token->end = parser->pos + 1;
parser->toksuper = token->parent;
break;
}
if (token->parent == -1) {
if (token->type != type || parser->toksuper == -1) {
return JSMN_ERROR_INVAL;
}
break;
}
token = &tokens[token->parent];
}
#else
for (i = parser->toknext - 1; i >= 0; i--) {
token = &tokens[i];
if (token->start != -1 && token->end == -1) {
if (token->type != type) {
return JSMN_ERROR_INVAL;
}
parser->toksuper = -1;
token->end = parser->pos + 1;
break;
}
}
/* Error if unmatched closing bracket */
if (i == -1) {
return JSMN_ERROR_INVAL;
}
for (; i >= 0; i--) {
token = &tokens[i];
if (token->start != -1 && token->end == -1) {
parser->toksuper = i;
break;
}
}
#endif
break;
case '\"':
r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
if (r < 0) {
return r;
}
count++;
if (parser->toksuper != -1 && tokens != NULL) {
tokens[parser->toksuper].size++;
}
break;
case '\t':
case '\r':
case '\n':
case ' ':
break;
case ':':
parser->toksuper = parser->toknext - 1;
break;
case ',':
if (tokens != NULL && parser->toksuper != -1 &&
tokens[parser->toksuper].type != JSMN_ARRAY &&
tokens[parser->toksuper].type != JSMN_OBJECT) {
#ifdef JSMN_PARENT_LINKS
parser->toksuper = tokens[parser->toksuper].parent;
#else
for (i = parser->toknext - 1; i >= 0; i--) {
if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
if (tokens[i].start != -1 && tokens[i].end == -1) {
parser->toksuper = i;
break;
}
}
}
#endif
}
break;
#ifdef JSMN_STRICT
/* In strict mode primitives are: numbers and booleans */
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case 't':
case 'f':
case 'n':
/* And they must not be keys of the object */
if (tokens != NULL && parser->toksuper != -1) {
const jsmntok_t *t = &tokens[parser->toksuper];
if (t->type == JSMN_OBJECT ||
(t->type == JSMN_STRING && t->size != 0)) {
return JSMN_ERROR_INVAL;
}
}
#else
/* In non-strict mode every unquoted value is a primitive */
default:
#endif
r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
if (r < 0) {
return r;
}
count++;
if (parser->toksuper != -1 && tokens != NULL) {
tokens[parser->toksuper].size++;
}
break;
#ifdef JSMN_STRICT
/* Unexpected char in strict mode */
default:
return JSMN_ERROR_INVAL;
#endif
}
}
if (tokens != NULL) {
for (i = parser->toknext - 1; i >= 0; i--) {
/* Unmatched opened object or array */
if (tokens[i].start != -1 && tokens[i].end == -1) {
return JSMN_ERROR_PART;
}
}
}
return count;
}
/**
* Creates a new parser based over a given buffer with an array of tokens
* available.
*/
JSMN_API void jsmn_init(jsmn_parser *parser) {
parser->pos = 0;
parser->toknext = 0;
parser->toksuper = -1;
}
#endif /* JSMN_HEADER */
#ifdef __cplusplus
}
#endif
#endif /* JSMN_H */

View File

@ -1,211 +0,0 @@
#include <zephyr.h>
#include <zephyr/net/socket.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <stdarg.h>
#define JSMN_PARENT_LINKS
#define JSMN_STRICT
#define JSMN_STATIC
#include "jsmn.h"
#include "sock.h"
#include "buf.h"
LOG_MODULE_REGISTER(msg);
enum cmdtype {
NONE,
IDENT,
CONSTS,
RAMP,
READ_ADC,
RESET_DAC,
RESET,
CMDTYPE_LEN
};
struct cmd {
enum cmdtype typ;
char *version;
char *msgid;
char *emsg;
union {
char *ident;
struct {
int P;
int I;
int Vnm;
} consts;
struct {
int dac;
int offset;
int dely;
} rampcmd;
int read_adc;
int reset_dac;
struct {
int id;
int blklen;
int siz;
} startscan;
};
};
struct parser {
struct readbuf *js; // JSON buffer.
jsmntok_t *ctok; // Parser is at this token.
jsmntok_t *last; // Last token in the sequence.
int cli; // Socket.
struct cmd cmd; // Command parsed into.
};
enum {
OK = 0,
CONVERR = BUF_WRITE_ERR,
SOCKERR = BUF_WRITE_ERR - 1
};
// Serialize struct cmd into JSON and send it.
static int
dispatch(int sock, struct cmd *cmd)
{
// Add byte for NUL terminator for snprintf()
char buf[CLIREADBUF_SIZ + sizeof(uint16_t) + 1] = {0};
struct bufptr bp = {buf, sizeof(buf)};
int r;
/* If no version could be read (i.e. sending a message
* saying that the JSON was invalid) then send a
* version 0 message.
*/
if (!cmd->version)
cmd->version = "0";
if ((r = buf_writef(&bp, "{\"version\":\"%s\"", cmd->version)) != BUF_OK)
return r;
if (cmd->msgid) {
if ((r = buf_writef(&bp, ",\"msgid\":\"%s\"", cmd->msgid)) != BUF_OK)
return r;
}
if (cmd->emsg) {
if ((r = buf_writef(&bp, ",\"error\":\"%s\", cmd->emsg)) != BUF_OK)
return r;
goto send;
}
if (!cmd->emsg) switch (cmd->typ) {
case IDENT:
if ((r = buf_writef(&bp, ",\"%s\":\"%s\", sl[IDENT].s, "cryosnom") != 0)
return r;
goto send;
case CONSTS:
// STUB
goto send;
case RAMPCMD:
// STUB
goto send;
case READ_ADC:
// STUB
goto send;
case READ_DAC:
// STUB
goto send;
case STARTSCAN:
// STUB
goto send;
}
send:
if ((r = buf_writef(&bp, "}")) != 0)
return r;
struct bufptr wptr = {buf, sizeof(buf) - bp.left};
if (!buf_write_sock(sock, &wptr))
return SOCKERR;
return OK;
}
static inline bool
eq(jsmntok_t *tk, char *buf, const char *s, size_t slen)
{
return (slen == buf->end - buf->start
&& strncasecmp(s, &jr->js[buf->start], slen) == 0);
}
#define liteq(buf,tok,st) liteq((buf), (tok), (st), sizeof(st) - 1)
static inline void
jsmn_term(char *buf, jsmntok_t *tok)
{
buf[tok->end] = 0;
}
static jsmntok_t
parse_consts(int sock, jsmntok_t *ct, int len, char *js)
{
while (len > 0) {
if (liteq(ct, js, "P")) {
ct++;
len--;
if (
}
static bool
parse(int sock, jsmntok_t *first, jsmntok_t *last, char *js)
{
if (first->type != JSMN_OBJECT) {
psr->obj.emsg = "malformed json (not an object)";
goto fail;
}
for (jsmntok_t *ct = first; ct < psr->last; ct++) {
if (liteq(ct, js, "version")) {
ct++;
if (!liteq(ct, js, "0")) {
psr->obj.emsg = "invalid version";
goto fail;
}
jsmn_term(js, ct);
psr->version = js + ct->start;
} else if (liteq(ct, js, "msgid")) {
ct++;
jsmn_term(js, ct);
psr->msgid = js + ct->start;
} else if (liteq(ct, js, "ident")) {
ct++;
psr->typ = IDENT;
} else if (liteq(ct, js, "consts")) {
ct++;
psr->typ = CONSTS;
if (ct->type == JSMN_OBJECT) {
if (!(ct = parse_consts(sock, ct+1, ct->size, js)))
goto fail;
}
}
}
fail:
return dispatch(&psr->obj);
}
/* Read a JSON message, parse it, execute it, and respond to it.
*/
bool
msg_parse_dispatch(int cli, struct readbuf *buf)
{
jsmn_parser psr;
jsmntok_t tokens[JSON_MAX_LEN];
struct cmd cmd = {
.typ = NONE
};
jsmn_init(&psr);
if (jsmn_parse(buf->buf, buf->readlen, &tokens, JSON_MAX_LEN) < 0) {
psr.emsg = "malformed json";
return dispatch(cli, &cmd);
}
return parse(cli, &tokens[0], &tokens[JSON_MAX_LEN-1], buf);
}

View File

@ -1,54 +0,0 @@
#pragma once
/* Controller only supports JSON_MAX_LEN JSON tokens.
* A token is
* * the beginning of an object
* * A key
* * any value
* * the beginning of an array
Due to a particular quirk of JSMN, the numbers
can also be strings. Numbers MUST be integers.
msgid SHOULD be a small number.
Messages are formatted like
{
"version" : num, // required, set to 0
"msgid" : str, // optional
"error" : str, // only from controller, ignored from computer
// and zero to one of
"ident" : null,
"consts" : { // all are optional
"P" : num,
"I" : num,
"Vnm" : num
}, // or null
"ramp" : {
"dac" : num,
"off" : num,
"dly" : num
},
"read_adc" : num,
"reset_dac" : num,
"reset_all" : null,
"startscan" : null, // from computer
"startscan" : { // from controller
"id" : int,
"blksiz" : int,
"blklen" : int
}
}
Both the controller and the computer read and write these messages.
*/
#define JSON_MAX_LEN 32
enum msg_ret {
MSG_OK,
MSG_BAD_JSON,
MSG_SOCKERR
};
enum msg_ret msg_parse_dispatch(int client, struct readbuf *buf);