484 lines
13 KiB
C
484 lines
13 KiB
C
/* *****************************************************************************
|
|
* packet/data.c -- data packet and buffer sending.
|
|
* 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 sending and receiving data buffers.
|
|
* ************************************************************************** */
|
|
#include <libp7/internals.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/* ************************************************************************** */
|
|
/* Main functions */
|
|
/* ************************************************************************** */
|
|
/**
|
|
* p7_send_data_packet:
|
|
* Send data packet.
|
|
*
|
|
* Carries 'raw' data in the context of a command. E.g. file data.
|
|
* Maximum data size is 256 octets.
|
|
*
|
|
* @arg handle the libp7 handle
|
|
* @arg total the total number of data packets in trans
|
|
* @arg id the packet id
|
|
* @arg data the data part
|
|
* @arg datasize the data part size (in bytes)
|
|
* @arg resp should listen to response (shifting-related)
|
|
* @return if it worked
|
|
*/
|
|
|
|
static int p7_send_data_packet(p7_handle_t *handle,
|
|
p7ushort_t total, p7ushort_t id,
|
|
const void *data, unsigned int datasize, int resp)
|
|
{
|
|
/* make new buffer */
|
|
unsigned char buf[8 + datasize];
|
|
p7_putascii(buf, total, 4);
|
|
p7_putascii(&buf[4], id, 4);
|
|
memcpy(&buf[8], data, datasize);
|
|
|
|
/* send packet */
|
|
return (p7_send_ext(handle, p7_pt_data, handle->_last_sent_command,
|
|
buf, 8 + datasize, resp));
|
|
}
|
|
|
|
/**
|
|
* p7_send_quick_data_packet:
|
|
* Send data packet.
|
|
*
|
|
* Carries 'raw' data in the context of a command. E.g. file data.
|
|
* Maximum data size is 256 octets. Buffer must have 8 spare bytes because
|
|
* that's the advantages of this function above the one before : it doesn't
|
|
* memcpy again.
|
|
*
|
|
* @arg handle the libp7 handle
|
|
* @arg total the total number of data packets in trans
|
|
* @arg id the packet id
|
|
* @arg buf the buffer with 8 spare bytes at the beginning
|
|
* @arg datasize the data part size (in bytes)
|
|
* @arg resp should listen to response (shifting-related)
|
|
* @return if it worked
|
|
*/
|
|
|
|
static int p7_send_quick_data_packet(p7_handle_t *handle,
|
|
p7ushort_t total, p7ushort_t id,
|
|
void *buf, p7ushort_t datasize, int resp)
|
|
{
|
|
/* make new buffer */
|
|
unsigned char *cbuf = buf;
|
|
p7_putascii(cbuf, total, 4);
|
|
p7_putascii(&cbuf[4], id, 4);
|
|
|
|
/* send packet */
|
|
return (p7_send_ext(handle, p7_pt_data, handle->_last_sent_command,
|
|
buf, 8 + datasize, resp));
|
|
}
|
|
|
|
/* ************************************************************************** */
|
|
/* Shifting utilities */
|
|
/* ************************************************************************** */
|
|
/**
|
|
* unshift:
|
|
* Unshift packet.
|
|
*
|
|
* @arg handle the libp7 handle
|
|
* @return if it worked
|
|
*/
|
|
|
|
static int unshift(p7_handle_t *handle)
|
|
{
|
|
/* truly unshift */
|
|
int err;
|
|
if ((err = p7_recv(handle, 1)))
|
|
return (err);
|
|
handle->_flags &= ~p7_intflag_shifted;
|
|
|
|
/* then return */
|
|
return (0);
|
|
}
|
|
|
|
/* ************************************************************************** */
|
|
/* Exchanging data functions */
|
|
/* ************************************************************************** */
|
|
#define BUFNUM 1024
|
|
|
|
/**
|
|
* p7_send_buffer:
|
|
* Part of the packet flows where data is sent.
|
|
*
|
|
* @arg handle the libp7 handle
|
|
* @arg buffer the buffer to read from
|
|
* @arg shift should shift?
|
|
* @arg disp the display callback
|
|
* @return the error (0 if ok)
|
|
*/
|
|
|
|
int p7_send_buffer(p7_handle_t *handle, const p7_buffer_t *buffer,
|
|
int shift, void (*disp)(p7ushort_t, p7ushort_t))
|
|
{
|
|
int err = 0;
|
|
|
|
/* dynamically allocate buffer */
|
|
unsigned int bufsize = MAX_RAWDATA_SIZE * BUFNUM;
|
|
bufsize = buffer->size < bufsize ? buffer->size : bufsize;
|
|
unsigned char *buf = malloc(8 + bufsize);
|
|
if (!buf) {
|
|
log_fatal("Couldn't allocate file buffer");
|
|
return (p7_error_alloc);
|
|
}
|
|
|
|
/* get total and last packet size */
|
|
unsigned int lastpacket_size = buffer->size % MAX_RAWDATA_SIZE;
|
|
unsigned int total = buffer->size / MAX_RAWDATA_SIZE
|
|
+ !!lastpacket_size; /* left data */
|
|
if (!lastpacket_size) lastpacket_size = MAX_RAWDATA_SIZE;
|
|
|
|
/* call disp for initialization */
|
|
if (disp) (*disp)(1, 0);
|
|
|
|
/* send data loop */
|
|
unsigned int datasize = MAX_RAWDATA_SIZE;
|
|
for (unsigned int id = 1; id <= total;) {
|
|
/* read big block */
|
|
if ((err = (*buffer->read)(buffer->cookie, 8 + buf, bufsize)))
|
|
return (err);
|
|
|
|
/* then send each block */
|
|
unsigned char *b = buf;
|
|
unsigned int bufnum = BUFNUM;
|
|
for (; bufnum-- && id <= total; id++) {
|
|
/* get datasize */
|
|
if (id == total) datasize = lastpacket_size;
|
|
|
|
/* logging */
|
|
log_info("sending packet %u/%u (%uo)", id, total, datasize);
|
|
|
|
/* displaying */
|
|
if (disp) (*disp)(id, total);
|
|
|
|
/* send data packet */
|
|
if ((err = p7_send_quick_data_packet(handle, total, id, b,
|
|
datasize, !(shift && id == 1)))) {
|
|
log_error("could not send data/receive response");
|
|
goto fail;
|
|
}
|
|
|
|
/* - check response - */
|
|
if (response.type != p7_pt_ack) {
|
|
log_error("calculator didn't send ack, weird");
|
|
err = p7_error_unknown;
|
|
goto fail;
|
|
}
|
|
|
|
/* increment buf pointer */
|
|
b += datasize;
|
|
}
|
|
}
|
|
|
|
/* unshift */
|
|
if (shift) err = unshift(handle);
|
|
|
|
/* free */
|
|
fail:
|
|
free(buf);
|
|
return (err);
|
|
}
|
|
|
|
/**
|
|
* p7_get_buffer:
|
|
* Part of the packet flows where data is received.
|
|
*
|
|
* Do not send an ack before going in this function, it will do it.
|
|
*
|
|
* @arg handle the libp7 handle
|
|
* @arg buffer the buffer to write to.
|
|
* @arg shift should shift?
|
|
* @arg disp the display callback
|
|
* @return the error (0 if ok)
|
|
*/
|
|
|
|
int p7_get_buffer(p7_handle_t *handle, const p7_buffer_t *buffer, p7uint_t size,
|
|
int shift, void (*disp)(p7ushort_t, p7ushort_t))
|
|
{
|
|
int err = 0;
|
|
|
|
/* announce */
|
|
if (buffer->announce && (err = (*buffer->announce)(buffer->cookie, size)))
|
|
return (err);
|
|
|
|
/* dynamically allocate buf size */
|
|
size_t bufsize = MAX_RAWDATA_SIZE * BUFNUM;
|
|
log_info("buffer size is %" PRIuSIZE, bufsize);
|
|
unsigned char *buf = malloc(bufsize);
|
|
if (!buf) {
|
|
log_fatal("Couldn't allocate file buffer");
|
|
return (p7_error_alloc);
|
|
}
|
|
|
|
/* call disp for initialization */
|
|
if (disp) (*disp)(1, 0);
|
|
|
|
/* responding with acks */
|
|
log_info("starting main loop");
|
|
unsigned char *b = buf; unsigned int fillsize = 0;
|
|
|
|
/* shift */
|
|
if (shift) p7_send_ack(handle, 0);
|
|
|
|
/* main loop */
|
|
while (size) {
|
|
/* send ack */
|
|
log_info("send ack");
|
|
if ((err = p7_send_ack(handle, 1))) {
|
|
log_fatal("couldn't send ack/didn't receive answer");
|
|
goto fail;
|
|
}
|
|
|
|
/* if packet is not data, wtf casio? */
|
|
if (response.type != p7_pt_data) {
|
|
log_fatal("packet type was unplanned, wtf ?");
|
|
err = p7_error_unknown;
|
|
goto fail;
|
|
}
|
|
|
|
/* displaying */
|
|
if (disp) (*disp)(response.id, response.total);
|
|
|
|
/* check overflow */
|
|
if ((size_t)response.data_size > size) {
|
|
log_fatal("%" PRIuP7SHORT " bytes received... "
|
|
"that's more than the %" PRIuSIZE " expected. "
|
|
"Taking the first bytes.",
|
|
response.data_size, size);
|
|
response.data_size = size;
|
|
}
|
|
|
|
/* copy the data */
|
|
log_info("is data, put it in buffer");
|
|
memcpy(b, response.data, response.data_size);
|
|
fillsize += response.data_size; b += response.data_size;
|
|
size -= response.data_size;
|
|
|
|
/* check if we should empty the buffer */
|
|
if (fillsize > bufsize - MAX_RAWDATA_SIZE || !size) {
|
|
log_info("buffer too full, should be emptied");
|
|
/* empty buffer in file
|
|
* TODO: read error and terminate if problem? */
|
|
(*buffer->write)(buffer->cookie, buf, fillsize);
|
|
b = buf; fillsize = 0;
|
|
}
|
|
}
|
|
|
|
/* send last ack */
|
|
if ((err = p7_send_ack(handle, 1))) {
|
|
log_fatal("couldn't send ack/didn't receive answer");
|
|
goto fail;
|
|
}
|
|
|
|
/* unshift */
|
|
if (shift) err = unshift(handle);
|
|
|
|
/* end */
|
|
fail:
|
|
free(buf);
|
|
return (err);
|
|
}
|
|
|
|
/* ************************************************************************** */
|
|
/* Exchanging data functions - buffer versions */
|
|
/* ************************************************************************** */
|
|
/**
|
|
* p7_send_data:
|
|
* Part of the packet flow where data is sent - buffer version.
|
|
*
|
|
* @arg handle the handle.
|
|
* @arg vbuf the buffer.
|
|
* @arg size the number of bytes of the buffer to send.
|
|
* @arg shift should shift?
|
|
* @arg disp the display
|
|
* @return the error code (0 if ok)
|
|
*/
|
|
|
|
int p7_send_data(p7_handle_t *handle, const void *vbuf, p7uint_t size,
|
|
int shift, void (*disp)(p7ushort_t, p7ushort_t))
|
|
{
|
|
int err = 0;
|
|
const unsigned char *buf = (const unsigned char*)vbuf;
|
|
|
|
/* get total and last packet size */
|
|
unsigned int lastpacket_size = size % MAX_RAWDATA_SIZE;
|
|
unsigned int total = size / MAX_RAWDATA_SIZE
|
|
+ !!lastpacket_size; /* left data */
|
|
if (!lastpacket_size) lastpacket_size = MAX_RAWDATA_SIZE;
|
|
|
|
/* call disp for initialization */
|
|
if (disp) (*disp)(1, 0);
|
|
|
|
/* send data loop */
|
|
unsigned int datasize = MAX_RAWDATA_SIZE;
|
|
for (unsigned int id = 0; id <= total;) {
|
|
/* get datasize */
|
|
if (id == total) datasize = lastpacket_size;
|
|
|
|
/* logging */
|
|
log_info("sending packet %u/%u (%uo)", id, total, datasize);
|
|
|
|
/* display */
|
|
if (disp) (*disp)(id, total);
|
|
|
|
/* send data packet */
|
|
if ((err = p7_send_data_packet(handle, total, id, buf, datasize,
|
|
!(shift && id == 1)))) {
|
|
log_error("couldn't send data/receive response");
|
|
return (err);
|
|
}
|
|
|
|
/* check response */
|
|
if (response.type != p7_pt_ack) {
|
|
log_error("calculator didn't send ack, weird");
|
|
return (p7_error_unknown);
|
|
}
|
|
|
|
/* increment buf pointer */
|
|
buf += datasize;
|
|
}
|
|
|
|
/* unshift */
|
|
if (shift) err = unshift(handle);
|
|
|
|
/* no error */
|
|
return (0);
|
|
}
|
|
|
|
/**
|
|
* p7_get_data:
|
|
* Part of the packet flow where data is received - buffer version.
|
|
*
|
|
* @arg handle the handle.
|
|
* @arg vbuf the final buffer (uncasted)
|
|
* @arg size the buffer size (and size to receive)
|
|
* @arg shift should shift?
|
|
* @arg disp the display
|
|
* @return the error code (0 if ok).
|
|
*/
|
|
|
|
int p7_get_data(p7_handle_t *handle, void *vbuf, p7uint_t size,
|
|
int shift, void (*disp)(p7ushort_t, p7ushort_t))
|
|
{
|
|
int err = 0;
|
|
unsigned char *buf = (unsigned char*)vbuf;
|
|
|
|
/* call disp for initialization */
|
|
if (disp) (*disp)(1, 0);
|
|
|
|
/* responding with acks */
|
|
log_info("starting main loop");
|
|
|
|
/* shift */
|
|
if (shift) p7_send_ack(handle, 0);
|
|
|
|
/* main loop */
|
|
while (size) {
|
|
/* send ack */
|
|
log_info("send ack");
|
|
if ((err = p7_send_ack(handle, 1))) {
|
|
log_fatal("couldn't send ack/didn't receive answer");
|
|
return (p7_error_unknown);
|
|
}
|
|
|
|
/* if packet is not data, wtf casio? */
|
|
if (response.type != p7_pt_data) {
|
|
log_fatal("packet type was unplanned, wtf ?");
|
|
return (p7_error_unknown);
|
|
}
|
|
|
|
/* displaying */
|
|
if (disp) (*disp)(response.id, response.total);
|
|
|
|
/* check overflow */
|
|
if (response.data_size > size) {
|
|
log_fatal("%" PRIuP7SHORT " bytes received... "
|
|
"that's more than the %" PRIuSIZE " expected. "
|
|
"Taking the first bytes.",
|
|
response.data_size, size);
|
|
response.data_size = size;
|
|
}
|
|
|
|
/* data! continue */
|
|
log_info("is data, put it in buffer");
|
|
memcpy(buf, response.data, response.data_size);
|
|
buf += response.data_size; size -= response.data_size;
|
|
}
|
|
|
|
/* send last ack */
|
|
if ((err = p7_send_ack(handle, 1))) {
|
|
log_fatal("couldn't send ack/didn't receive answer");
|
|
return (p7_error_unknown);
|
|
}
|
|
|
|
/* unshift */
|
|
if (shift)
|
|
err = unshift(handle);
|
|
|
|
/* end */
|
|
return (err);
|
|
}
|
|
|
|
/* ************************************************************************** */
|
|
/* Decode a data packet data */
|
|
/* ************************************************************************** */
|
|
/* Layout of it */
|
|
typedef struct {
|
|
unsigned char total_number[4];
|
|
unsigned char current_number[4];
|
|
unsigned char data[];
|
|
} packetdata_data_t;
|
|
|
|
/**
|
|
* decode_data:
|
|
* Get data from data packet data field.
|
|
*
|
|
* @arg handle the handle
|
|
* @arg raw raw data
|
|
* @arg raw_size raw data size
|
|
* @return if there was an error.
|
|
*/
|
|
|
|
int p7_decode_data(p7_handle_t *handle,
|
|
const void *raw, p7ushort_t raw_size)
|
|
{
|
|
p7_packet_t *packet = &handle->_response;
|
|
const packetdata_data_t *d = raw;
|
|
|
|
/* total number */
|
|
packet->total = p7_getascii(d->total_number, 4);
|
|
log_info("Total data packets : %" PRIuP7SHORT, packet->total);
|
|
|
|
/* current id */
|
|
packet->id = p7_getascii(d->current_number, 4);
|
|
log_info("Data packet ID : %" PRIuP7SHORT, packet->id);
|
|
|
|
/* data */
|
|
packet->data_size = raw_size - 8;
|
|
memcpy(packet->data, d->data, raw_size - 8);
|
|
log_info("Decoded data (%" PRIuP7SHORT "o) :", packet->data_size);
|
|
logm_info(packet->data, packet->data_size);
|
|
|
|
/* no error */
|
|
return (0);
|
|
}
|