cake
/
libp7
Archived
1
0
Fork 1
This repository has been archived on 2024-03-16. You can view files and clone it, but cannot push or open issues or pull requests.
libp7/src/protocol/seven/recv.c

547 lines
14 KiB
C

/* *****************************************************************************
* protocol/seven/recv.c -- receive and decode the packet.
* Copyright (C) 2016-2017 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
*
* This file is part of libp7.
* libp7 is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3.0 of the License,
* or (at your option) any later version.
*
* libp7 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libp7; if not, see <http://www.gnu.org/licenses/>.
*
* These functions are about receiving and decoding a packet.
* It is also about managing checksum problems and timeouts.
* ************************************************************************** */
#include <libp7/internals.h>
#include <stdlib.h>
#include <string.h>
#if LOGLEVEL <= ll_info
/* ************************************************************************** */
/* Logging */
/* ************************************************************************** */
/**
* getcmdstring:
* Get command code string (useful for logging).
*
* @arg code command code
* @return the string
*/
static const char *getcmdstring(unsigned int code)
{
static const char *commands[] = {
/* system commands */
"restart the equipment",
"get device info",
"set link settings",
NULL, NULL, NULL,
"set link timeout",
"OS Update verification #1",
"OS Update verification #2",
"OS Update verification #3",
"OS Update verification #4",
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* MCS commands */
"create directory on mcs",
"delete directory on mcs",
"rename directory on mcs",
"change working directory on mcs",
"request file transfer from mcs",
"transfer file on mcs",
"delete file on mcs",
"rename file on mcs",
"copy file on mcs",
"request all files transfer on mcs",
"reset mcs",
"request mcs capacity",
"transmit mcs capacity",
"request all mcs file info",
"transfer all mcs file info",
"request RAM image",
"transfer RAM image",
"request setup entry",
"transfer setup entry",
"request all setup entry",
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* Flash commands */
"create directory on flash",
"delete directory on flash",
"rename directory on flash",
"change working directory on flash",
"request flash file",
"transfer flash file",
"delete file on flash",
"rename file on flash",
"copy file on flash",
"request all flash file",
"reset flash",
"capacity transmit request",
"capacity transmit",
"request flash file info",
"transfer flash file info",
"request ROM image",
"transfer ROM image",
"optimize flash filesystem",
/* OS Update */
"request CASIOWIN entry",
"send CASIOWIN entry",
"request boot code",
"send boot code",
"upload and run"
};
/* return the string */
if (code > 0x56 || !commands[code])
return ("<unknown>");
return (commands[code]);
}
/**
* geterrstring:
* Get error string (useful for logging).
*
* @arg code error code
* @return the string
*/
static const char *geterrstring(p7_seven_error_t code)
{
static const char *strings[] = {
"should resend",
"file exists, overwrite request",
"will not overwrite",
"overwrite impossible",
"memory is full"
};
if (code > 0x04) return ("<unknown error>");
return (strings[code]);
}
/**
* gettermstring:
* Get termination cause string (useful for logging).
*
* @arg code cause code
* @return the string
*/
static const char *gettermstring(p7_seven_term_t code)
{
static const char *strings[] = {
"default",
"user requested",
"timeouts",
"on overwrite request"
};
if (code > 0x03) return ("<unknown termination reason>");
return (strings[code]);
}
#endif
/* ************************************************************************** */
/* Receiving utilities */
/* ************************************************************************** */
#pragma pack(1)
/* TYPZ1/2 subheader */
struct typz_subheader {
/* ASCII-hex size */
uint8_t size[6];
/* dimensions */
uint8_t height[4];
uint8_t width[4];
/* one: we are number one but it is in ascii (always "1") */
uint8_t one;
/* encoding algorithm:
* - RC2: 16-bit mode;
* - RC3: 3-bit mode (1 nib./pxl, red-green-blue-trash)
* - RM2: 2-bit mode? */
uint8_t enc[3];
};
#pragma pack()
/**
* complete_packet:
* Complete the packet with N bytes.
*
* Uses stream buffering to achieve this.
*
* @arg handle the libp7 handle.
* @arg buf the buffer.
* @arg recv pointer to currently received bytes.
* @arg with complete with this number of byte.
* @return the error (0 if ok)
*/
static int complete_packet(p7_handle_t *handle,
unsigned char *buf, size_t *recv, size_t with)
{
int err = p7_read(&handle->_stream, &buf[*recv], with);
*recv += with;
return (err);
}
/* ************************************************************************** */
/* Main receiving functions */
/* ************************************************************************** */
/**
* p7_seven_recv_main:
* Receive a packet.
*
* This is the main receiving function. It receives, parses, and returns
* if everything has been successfully done or not.
* It uses buffered I/O to read progressively all of the packet.
*
* @arg handle the libp7 handler
* @return the error (0 if ok)
*/
#define buffer handle->_recv_buffer
#define COMPLETE_PACKET(N) { \
int COMP_PACKET_err; \
if ((COMP_PACKET_err = complete_packet(handle, \
buffer, &received, (N)))) \
return (COMP_PACKET_err); \
}
static int p7_seven_recv_main(p7_handle_t *handle)
{
/* prepare reception */
size_t received = 0;
/* log packet */
log_info("receiving packet...");
/* get first three bytes, check if is CAL */
do {
COMPLETE_PACKET(3)
if (memcmp(buffer, "CAL", 3))
break;
/* get the CAL value */
log_info("is the magic CAL!");
COMPLETE_PACKET(4) /* TODO: check if is 00D0, and what is it? */
log_info("CAL value is %.4s", &buffer[3]);
handle->_flags |= p7_intflag_nocheck;
/* good as new! */
received = 0;
log_info("Restart receiving the packet.");
} while (1);
/* adjust */
if (handle->_flags & p7_intflag_adjscreen) {
/* adjust. */
log_info("Adjusting: looking for the screen header.");
log_info("Current buffer:");
logm_info(buffer, 3);
while (memcmp(buffer, "\x0BTY", 3)) {
buffer[0] = buffer[1];
buffer[1] = buffer[2];
received = 2;
COMPLETE_PACKET(1)
log_info("Current buffer:");
logm_info(buffer, 3);
}
}
/* get the type */
response.type = buffer[0];
/* image has a particular format starting from here, look for it now! */
if (response.type == p7_seven_type_ohp) {
log_info("Johnson, this is a screen.");
COMPLETE_PACKET(3)
/* get image by its type */
unsigned int image_size = 0;
if (!memcmp(&buffer[1], "TYP01", 5)) {
log_info("This is normal VRAM, we know it !");
response.pictype = p7_pictype_1bit;
response.width = 128;
response.height = 64;
image_size = 1024;
} else if (!memcmp(&buffer[1], "TYPZ1", 5)
|| !memcmp(&buffer[1], "TYPZ2", 5)) {
log_info("Prizm VRAM!");
COMPLETE_PACKET(sizeof(struct typz_subheader))
struct typz_subheader *s = (void*)&buffer[6];
response.width = p7_getascii(s->width, 4);
response.height = p7_getascii(s->height, 4);
/* check the type */
response.pictype = 0;
if (!memcmp(s->enc, "RC2", 3))
response.pictype = p7_pictype_16bit;
else if (!memcmp(s->enc, "RC3", 3))
response.pictype = p7_pictype_3bit;
else if (!memcmp(s->enc, "RM2", 3))
response.pictype = p7_pictype_2bit;
#if LOGLEVEL <= ll_error
else log_error("Unknown encoding: %.3s", s->enc);
#endif
/* FIXME: check size according to format? */
image_size = p7_getascii(s->size, 6);
log_info("Image size: %uB", image_size);
handle->_flags |= p7_intflag_nocheck;
} else {
log_error("Unknown picture encoding: %.5s", &buffer[1]);
return (p7_error_checksum);
}
/* check if we should skip */
if (!response.pictype || image_size > MAX_VRAM_SIZE) {
p7_skip(&handle->_stream, image_size); /* FIXME: checksum? */
return (p7_error_unknown);
}
/* complete packet */
size_t hdsize = received;
log_info("Get screen content (%uo)", image_size);
COMPLETE_PACKET(image_size)
memcpy(&response.vram, &buffer[hdsize], image_size);
/* check the sum */
if ((~handle->_flags & p7_intflag_nocheck)) {
COMPLETE_PACKET(2)
/* calculate the checksums */
p7uint_t csum = p7_checksum(buffer, received);
p7uint_t csum_ex = p7_getascii(&buffer[received - 2], 2);
/* check them */
if (csum != csum_ex) {
log_error("Checksum problem: expected 0x%02" PRIXP7INT ", "
"got 0x%02" PRIXP7INT, csum_ex, csum);
return (p7_error_checksum);
}
}
handle->_flags &= ~p7_intflag_nocheck;
/* log */
log_info("received the following [screen] packet (%" PRIuSIZE "o) :",
received);
logm_info(buffer, received);
/* and return the packet */
return (0);
}
/* subtype and extended */
COMPLETE_PACKET(1)
p7ushort_t subtype = p7_getascii(&buffer[1], 2);
/* - extended (beginning) - */
p7ushort_t data_size = 0;
int is_extended = (buffer[3] == '1');
if (is_extended) {
/* get data size */
COMPLETE_PACKET(4)
data_size = p7_getascii(&buffer[4], 4);
/* check data size */
if (data_size > MAX_ENCDFLD_SIZE) {
int err = p7_skip(&handle->_stream, data_size + 2);
if (err) return (err);
return (p7_error_checksum);
}
/* get data */
COMPLETE_PACKET(data_size + 2)
} else
COMPLETE_PACKET(2)
/* check if we should read a binary zero */
int is_binary_zero = (response.type == p7_seven_type_cmd
&& subtype == p7_seven_cmdosu_upandrun);
if (is_binary_zero) COMPLETE_PACKET(1)
/* log */
p7ushort_t packet_size = 6 + 4 * !!is_extended + data_size;
log_info("received the following [normal] packet (%" PRIuP7SHORT "o) :",
packet_size);
logm_info(buffer, packet_size);
/* calculate checksum */
unsigned int calculated_checksum = p7_checksum(buffer, packet_size);
unsigned int found_checksum = p7_getascii(&buffer[packet_size - 2], 2);
if (calculated_checksum != found_checksum)
return (p7_error_checksum);
/* - extended (finish) - */
if (is_extended)
data_size = p7_decode(&buffer[8], &buffer[8], data_size);
/* get fields out for specific packets */
switch (response.type) {
/* - for command - */
case p7_seven_type_cmd:
log_info("packet was interpreted as a command one");
response.code = subtype;
log_info("command is '%s' (0x%02" PRIxP7SHORT ")",
getcmdstring(subtype), subtype);
if (is_extended
&& p7_seven_decode_command(handle, &buffer[8], data_size))
return (p7_error_checksum);
break;
/* - for data - */
case p7_seven_type_data:
log_info("packet was interpreted as a data one");
p7_seven_decode_data(handle, &buffer[8], data_size);
break;
/* - for roleswap - */
case p7_seven_type_roleswp:
log_info("packet was interpreted as a roleswap one");
log_info("becoming active again");
handle->_flags |= p7_intflag_active;
break;
/* - for check - */
case p7_seven_type_check:
log_info("packet was interpreted as a check one");
response.initial = !subtype;
break;
/* - for ack - */
case p7_seven_type_ack:
log_info("packet was interpreted as an ack one");
response.ow = (subtype == 0x01);
response.extended = (subtype == 0x02);
if (response.extended
&& p7_seven_decode_ack(handle, &buffer[8], data_size))
return (p7_error_checksum);
if (subtype == 0x03)
handle->_flags |= p7_intflag_terminated;
break;
/* - for error - */
case p7_seven_type_error:
log_info("packet was interpreted as an error one");
log_info("error is '%s' (0x%02" PRIxP7SHORT ")",
geterrstring(subtype), subtype);
response.code = subtype;
if (subtype == p7_seven_err_fullmem)
handle->_flags |= p7_intflag_terminated;
break;
/* - for termination - */
case p7_seven_type_end:
log_info("packet was interpreted as a terminate one");
log_info("termination cause is '%s' (0x%02" PRIxP7SHORT ")",
gettermstring(subtype), subtype);
response.code = subtype;
if (subtype == p7_seven_term_user)
return (p7_error_interrupted);
break;
/* other */
default:
log_fatal("bad type received ! type was 0x%02x", response.type);
return (p7_error_unknown);
}
/* finally, return the packet */
return (0);
}
/**
* p7_seven_recv:
* Receives packet, checks for errors.
*
* @arg handle the libp7 handler
* @arg checksum if 0 and invalid checksum, ignore and receive again
* @return if it worked
*/
int p7_seven_recv(p7_handle_t *handle, int checksum)
{
/* make checks */
chk_handle(handle);
/* main receiving loop */
int tries = 2;
int wasresend = 0, err;
do {
/* get packet */
err = p7_seven_recv_main(handle);
/* if there was a fail */
switch (err) {
/* no error :D */
case p7_noerror:
tries = 2;
break;
/* if calc couldn't be found, terminate communication */
case p7_error_nocalc:
tries = 0;
break;
/* if it is a timeout, remove a try */
case p7_error_timeout:
tries--;
/* send a check */
err = p7_seven_send_check(handle);
if (err) return (err);
/* set things */
wasresend = 0;
err = p7_error_timeout;
break;
/* if it is a bad checksum, ask for resend ; also,
* reset tries number in case of bad checksum between timeouts */
case p7_error_checksum:
/* if we shouldn't bother sending resend error, just
* transmit checksum error */
if (!checksum) break;
/* if we receive an invalid checksum while we are in packet
* shifting mode, or if we received an error when we sent a
* resend error, cut connexion... we can't do anything. */
if ((handle->_flags & p7_intflag_shifted) || wasresend)
return (p7_error_irrecoverable);
/* otherwise, check tries and send resend error */
tries--;
err = p7_seven_send_err_resend(handle);
if (err) return (err);
/* set things */
wasresend = 1;
err = p7_error_checksum;
break;
/* should not catch bad type & others */
default:
return (p7_error_unknown);
}
} while (err && tries);
/* return the error */
return (err);
}