554 lines
16 KiB
C
554 lines
16 KiB
C
/* ************************************************************************** */
|
|
/* _____ _ */
|
|
/* packet/recv.c |_ _|__ _ _| |__ ___ _ _ */
|
|
/* | Project: libp7 | |/ _ \| | | | '_ \ / _ \ | | | */
|
|
/* | | (_) | |_| | | | | __/ |_| | */
|
|
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
|
|
/* Last updated: 2016/10/13 07:33:17 |___/ */
|
|
/* */
|
|
/* ************************************************************************** */
|
|
#include <libp7/internals.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/* ************************************************************************** */
|
|
/* Data layouts */
|
|
/* ************************************************************************** */
|
|
/**
|
|
* packetdata_ackext_t:
|
|
* The extended ack packet data field layout.
|
|
*/
|
|
|
|
typedef struct {
|
|
/* hardware identifier - ASCII */
|
|
unsigned char hwid[8];
|
|
/* processor identifier - ASCII */
|
|
unsigned char cpuid[16];
|
|
|
|
/* preprogrammed ROM capacity - ASCII-hex (Ko) */
|
|
unsigned char preprog_rom_capacity[8];
|
|
/* flash ROM capacity - ASCII-hex (Ko) */
|
|
unsigned char flash_rom_capacity[8];
|
|
/* RAM capacity - ASCII-hex (Ko) */
|
|
unsigned char ram_capacity[8];
|
|
|
|
/* preprogrammed ROM version - "xx.xx.xx" + type */
|
|
unsigned char preprog_rom_version[16];
|
|
|
|
/* bootcode version - "xx.xx.xx" + type */
|
|
unsigned char bootcode_version[16];
|
|
/* bootcode offset - ASCII-hex */
|
|
unsigned char bootcode_offset[8];
|
|
/* bootcode size - ASCII-hex (Ko) */
|
|
unsigned char bootcode_size[8];
|
|
|
|
/* OS version - "xx.xx.xx" + type */
|
|
unsigned char os_version[16];
|
|
/* OS offset - ASCII-hex */
|
|
unsigned char os_offset[8];
|
|
/* OS size - ASCII-hex (Ko) */
|
|
unsigned char os_size[8];
|
|
|
|
/* protocol version - "x.xx" */
|
|
unsigned char protocol_version[4];
|
|
/* product ID */
|
|
unsigned char product_id[16];
|
|
/* name set by user in SYSTEM */
|
|
unsigned char username[16];
|
|
} packetdata_ackext_t;
|
|
|
|
/**
|
|
* packetdata_data_t:
|
|
* The data packet data field layout.
|
|
*/
|
|
|
|
typedef struct {
|
|
unsigned char total_number[4];
|
|
unsigned char current_number[4];
|
|
unsigned char data[];
|
|
} packetdata_data_t;
|
|
|
|
/* ************************************************************************** */
|
|
/* Useful functions */
|
|
/* ************************************************************************** */
|
|
/**
|
|
* p7_recv_getver:
|
|
* Get version from 16-bytes char buffer.
|
|
*
|
|
* data is not a string as it doesn't finish with an '\0'.
|
|
*
|
|
* @arg ver the version destination
|
|
* @arg data the raw version data ("xx.xx.xx" + type)
|
|
*/
|
|
|
|
static void p7_recv_getver(p7_version_t *ver, unsigned char data[16])
|
|
{
|
|
char *t;
|
|
|
|
/* get major */
|
|
ver->major = p7_getascii(data, 2);
|
|
/* get minor */
|
|
ver->minor = p7_getascii(&data[3], 2);
|
|
/* get rev */
|
|
ver->rev = p7_getascii(&data[6], 2);
|
|
/* get type */
|
|
memcpy(&ver->type, &data[8], 8);
|
|
ver->type[8] = '\0';
|
|
if ((t = strchr(ver->type, 0xFF))) *t = '\0';
|
|
|
|
/* log */
|
|
log_info("major: 0x%02x, minor: 0x%02x, rev: 0x%02x (type: '%s')",
|
|
ver->major, ver->minor, ver->rev, ver->type);
|
|
}
|
|
|
|
/**
|
|
* p7_recv_getack:
|
|
* Get data from ack data field.
|
|
*
|
|
* Layout is described in the fxReverse projet documentation.
|
|
*
|
|
* @arg ack the ack packet destination
|
|
* @arg data the raw data
|
|
*/
|
|
|
|
static void p7_recv_getack(p7_packetack_t *ack, void *data)
|
|
{
|
|
packetdata_ackext_t *d = data;
|
|
char *t; /* temporary char pointer */
|
|
|
|
/* log */
|
|
log_info("ack packet is extended");
|
|
|
|
/* hardware identifier */
|
|
memcpy(ack->hwid, d->hwid, 8); ack->hwid[8] = '\0';
|
|
log_info("hardware identifier is '%s'", ack->hwid);
|
|
/* processor identifier */
|
|
memcpy(ack->cpuid, d->cpuid, 16); ack->cpuid[16] = '\0';
|
|
log_info("cpu identifier is '%s'", ack->cpuid);
|
|
|
|
/* preprogrammed ROM capacity */
|
|
ack->preprog_rom_capacity = p7_getascii(d->preprog_rom_capacity, 8) * 1000;
|
|
log_info("preprogrammed ROM capacity is %luo",
|
|
ack->preprog_rom_capacity);
|
|
/* preprogrammed ROM version */
|
|
log_info("preprogrammed ROM version is the following :");
|
|
p7_recv_getver(&ack->preprog_rom_version, d->preprog_rom_version);
|
|
/* preprogrammed ROM looks wiped out */
|
|
ack->preprog_rom_wiped = (ack->preprog_rom_capacity == 0);
|
|
if (ack->preprog_rom_wiped) {
|
|
log_warn("preprogrammed ROM info looks wiped out !");
|
|
}
|
|
|
|
/* flash ROM capacity */
|
|
ack->flash_rom_capacity = p7_getascii(d->flash_rom_capacity, 8) * 1000;
|
|
log_info("flash ROM capacity is %luo",
|
|
ack->flash_rom_capacity);
|
|
/* RAM capacity */
|
|
ack->ram_capacity = p7_getascii(d->ram_capacity, 8);
|
|
log_info("RAM capacity is %luo", ack->ram_capacity);
|
|
|
|
/* bootcode looks wiped out */
|
|
ack->bootcode_wiped = (d->bootcode_version[2] == 0xFF);
|
|
if (ack->bootcode_wiped) { log_warn("bootcode info looks wiped out !"); }
|
|
/* bootcode version */
|
|
log_info("bootcode version is the following :");
|
|
p7_recv_getver(&ack->bootcode_version, d->bootcode_version);
|
|
/* bootcode offset */
|
|
ack->bootcode_offset = p7_getascii(d->bootcode_offset, 8);
|
|
log_info("bootcode offset is %lu", ack->bootcode_offset);
|
|
/* bootcode size */
|
|
ack->bootcode_size = p7_getascii(d->bootcode_size, 8);
|
|
log_info("bootcode size is %lu", ack->bootcode_size);
|
|
|
|
/* OS version */
|
|
log_info("OS version is the following :");
|
|
p7_recv_getver(&ack->os_version, d->os_version);
|
|
/* OS offset */
|
|
ack->os_offset = p7_getascii(d->os_offset, 8);
|
|
log_info("OS offset is 0x%08lx", ack->os_offset);
|
|
/* OS size */
|
|
ack->os_size = p7_getascii(d->os_size, 8);
|
|
log_info("OS size is %luo", ack->os_size);
|
|
|
|
/* protocol version */
|
|
ack->protocol_version.major = p7_getascii(d->protocol_version, 1);
|
|
ack->protocol_version.minor = p7_getascii(&d->protocol_version[2], 2);
|
|
log_info("protocol version is %d.%02d",
|
|
ack->protocol_version.major, ack->protocol_version.minor);
|
|
/* product ID */
|
|
memcpy(ack->product_id, d->product_id, 16); ack->product_id[16] = '\0';
|
|
if ((t = strchr(ack->product_id, 0xFF))) *t = '\0';
|
|
log_info("product ID is %s", ack->product_id);
|
|
/* username */
|
|
memcpy(ack->username, d->username, 16); ack->username[16] = '\0';
|
|
if ((t = strchr(ack->username, 0xFF))) *t = '\0';
|
|
log_info("username is %s", ack->username);
|
|
}
|
|
|
|
/**
|
|
* p7_recv_getcmd:
|
|
* Get data from command data field.
|
|
*
|
|
* Layout is described in the fxReverse projet documentation.
|
|
*
|
|
* @arg cmd the command packet destination
|
|
* @arg raw the raw data
|
|
* @arg raw_size the raw data size
|
|
* @return if it was correct
|
|
*/
|
|
|
|
static int p7_recv_getcmd(p7_packetcommand_t *cmd, const unsigned char *raw,
|
|
size_t raw_size)
|
|
{
|
|
/* check up_and_run command */
|
|
if (cmd->code == p7_cc_osu_upandrun) {
|
|
if (raw_size != 12) return (0);
|
|
cmd->uploadsize = p7_getascii(raw, 4);
|
|
cmd->loadaddr = p7_getascii(&raw[4], 4);
|
|
cmd->straddr = p7_getascii(&raw[8], 4);
|
|
return (1);
|
|
}
|
|
|
|
/* check minimum size */
|
|
if (raw_size < 24) return (0);
|
|
|
|
/* get overwrite */
|
|
cmd->overwrite = p7_getascii(raw, 2);
|
|
log_info("overwrite mode is '%s' (0x%02x)",
|
|
p7_getowstring(cmd->overwrite), cmd->overwrite);
|
|
|
|
/* get data type (?) */
|
|
cmd->datatype = p7_getascii(&raw[2], 2);
|
|
log_info("datatype is *todo: put MCS datatypes* (0x%02x)", cmd->datatype);
|
|
|
|
/* filesize */
|
|
cmd->filesize = p7_getascii(&raw[4], 8);
|
|
log_info("filesize is %luo", cmd->filesize);
|
|
|
|
/* args length */
|
|
size_t l1 = p7_getascii(&raw[12], 2);
|
|
size_t l2 = p7_getascii(&raw[14], 2);
|
|
size_t l3 = p7_getascii(&raw[16], 2);
|
|
size_t l4 = p7_getascii(&raw[18], 2);
|
|
size_t l5 = p7_getascii(&raw[20], 2);
|
|
size_t l6 = p7_getascii(&raw[22], 2);
|
|
|
|
/* re-check size */
|
|
if (raw_size != 24 + l1 + l2 + l3 + l4 + l5 + l6) return (0);
|
|
|
|
/* args */
|
|
const unsigned char *p = &raw[24];
|
|
memcpy(cmd->args[0], p, l1); cmd->args[0][l1] = '\0'; p += l1;
|
|
memcpy(cmd->args[1], p, l2); cmd->args[1][l2] = '\0'; p += l2;
|
|
memcpy(cmd->args[2], p, l3); cmd->args[2][l3] = '\0'; p += l3;
|
|
memcpy(cmd->args[3], p, l4); cmd->args[3][l4] = '\0'; p += l4;
|
|
memcpy(cmd->args[4], p, l5); cmd->args[4][l5] = '\0'; p += l5;
|
|
memcpy(cmd->args[5], p, l6); cmd->args[5][l6] = '\0';
|
|
|
|
/* log args */
|
|
if (l1) { log_info("D1 is '%s'", cmd->args[0]); }
|
|
if (l2) { log_info("D2 is '%s'", cmd->args[1]); }
|
|
if (l3) { log_info("D3 is '%s'", cmd->args[2]); }
|
|
if (l4) { log_info("D4 is '%s'", cmd->args[3]); }
|
|
if (l5) { log_info("D5 is '%s'", cmd->args[4]); }
|
|
if (l6) { log_info("D6 is '%s'", cmd->args[5]); }
|
|
|
|
/* it worked */
|
|
return (1);
|
|
}
|
|
|
|
/**
|
|
* p7_recv_getdata:
|
|
* Get data from data packet data field.
|
|
*
|
|
* @arg data organized data with fields
|
|
* @arg raw raw data
|
|
* @arg raw_size raw data size
|
|
*/
|
|
|
|
static void p7_recv_getdata(p7_packetdata_t *data, void *raw,
|
|
p7ushort_t raw_size)
|
|
{
|
|
packetdata_data_t *d = raw;
|
|
|
|
/* total number */
|
|
data->total = p7_getascii(d->total_number, 4);
|
|
log_info("Total data packets : %lu", data->total);
|
|
|
|
/* current id */
|
|
data->id = p7_getascii(d->current_number, 4);
|
|
log_info("Data packet ID : %lu", data->id);
|
|
|
|
/* data */
|
|
data->size = raw_size - 8;
|
|
memcpy(data->data, d->data, raw_size - 8);
|
|
log_info("Decoded data (%luo) :", data->size);
|
|
logm_info(data->data, data->size);
|
|
}
|
|
|
|
/* ************************************************************************** */
|
|
/* Main functions */
|
|
/* ************************************************************************** */
|
|
/**
|
|
* complete_packet:
|
|
* Complete the packet with N bytes.
|
|
*
|
|
* Uses stream buffering to achieve this.
|
|
*
|
|
* @arg stream the P7 stream.
|
|
* @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_stream_t *stream,
|
|
unsigned char *buf, size_t *recv, size_t with)
|
|
{
|
|
int err = p7_read(stream, &buf[*recv], with);
|
|
*recv += with;
|
|
return (err);
|
|
}
|
|
|
|
/**
|
|
* p7_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->_stream, \
|
|
buffer, &received, (N)))) \
|
|
return (COMP_PACKET_err); \
|
|
}
|
|
|
|
static int p7_recv_main(p7_handle_t *handle)
|
|
{
|
|
/* prepare reception */
|
|
size_t received = 0;
|
|
|
|
/* log packet */
|
|
log_info("receiving packet...");
|
|
|
|
/* check type */
|
|
COMPLETE_PACKET(1)
|
|
response.type = buffer[0];
|
|
|
|
/* image has a particular format starting from here, look for it now! */
|
|
if (response.type == p7_pt_ohp) {
|
|
COMPLETE_PACKET(5)
|
|
/* 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.screen.type = p7_pscr_typ01;
|
|
image_size = 1024;
|
|
} else {
|
|
log_error("Unknown image type : %.5s", &buffer[1]);
|
|
return (p7_error_checksum);
|
|
}
|
|
|
|
/* complete packet */
|
|
log_info("Get screen and checksum (%uo)", image_size + 2);
|
|
COMPLETE_PACKET(image_size + 2)
|
|
memcpy(&response.screen.vram, &buffer[6], image_size);
|
|
|
|
/* log */
|
|
unsigned int packet_size = 6 + image_size + 2;
|
|
log_info("received the following [screen] packet (%uo) :", packet_size);
|
|
logm_info(buffer, packet_size);
|
|
|
|
/* check the sum */
|
|
if (!image_size
|
|
|| p7_checksum(buffer, packet_size)
|
|
!= p7_getascii(&buffer[packet_size - 2], 2))
|
|
return (p7_error_checksum);
|
|
|
|
/* and return the packet */
|
|
return (0);
|
|
}
|
|
|
|
/* subtype and extended */
|
|
COMPLETE_PACKET(3)
|
|
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);
|
|
|
|
/* 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_pt_cmd
|
|
&& subtype == p7_cc_osu_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 (%luo) :", 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_pt_cmd:
|
|
log_info("packet was interpreted as a command one");
|
|
response.command.code = subtype;
|
|
log_info("command is '%s' (0x%02lx)", p7_getcmdstring(subtype), subtype);
|
|
if (is_extended
|
|
&& !p7_recv_getcmd(&response.command, &buffer[8], data_size))
|
|
return (p7_error_checksum);
|
|
break;
|
|
|
|
/* - for data - */
|
|
case p7_pt_data:
|
|
log_info("packet was interpreted as a data one");
|
|
p7_recv_getdata(&response.data, &buffer[8], data_size);
|
|
break;
|
|
|
|
/* - for roleswap - */
|
|
case p7_pt_roleswp:
|
|
log_info("packet was interpreted as a roleswap one");
|
|
log_info("becoming active again");
|
|
handle->_active = 1;
|
|
break;
|
|
|
|
/* - for check - */
|
|
case p7_pt_check:
|
|
log_info("packet was interpreted as a check one");
|
|
response.check.initial = !subtype;
|
|
break;
|
|
|
|
/* - for ack - */
|
|
case p7_pt_ack:
|
|
log_info("packet was interpreted as an ack one");
|
|
response.ack.overwrite = (subtype == 0x01);
|
|
response.ack.extended = (subtype == 0x02);
|
|
if (response.ack.extended) p7_recv_getack(&response.ack, &buffer[8]);
|
|
break;
|
|
|
|
/* - for error - */
|
|
case p7_pt_error:
|
|
log_info("packet was interpreted as an error one");
|
|
log_info("error is '%s' (0x%02lx)",
|
|
p7_geterrstring(subtype), subtype);
|
|
response.error.code = subtype;
|
|
break;
|
|
|
|
/* - for termination - */
|
|
case p7_pt_terminate:
|
|
log_info("packet was interpreted as a terminate one");
|
|
log_info("termination cause is '%s' (0x%02lx)",
|
|
p7_gettermstring(subtype), subtype);
|
|
response.term.reason = subtype;
|
|
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_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_recv(p7_handle_t *handle, int checksum)
|
|
{
|
|
/* check if handler is initialized */
|
|
if (!handle)
|
|
return (p7_error_uninitialized);
|
|
|
|
/* main receiving loop */
|
|
int tries = 2, err = 0;
|
|
int wasresend = 0;
|
|
do {
|
|
/* get packet */
|
|
err = p7_recv_main(handle);
|
|
|
|
/* if there was a fail */
|
|
if (err) switch (err) {
|
|
/* 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_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->_shifted || wasresend)
|
|
return (p7_error_irrecoverable);
|
|
|
|
/* otherwise, send resend error */
|
|
err = p7_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);
|
|
}
|