cake
/
libcasio
Archived
1
1
Fork 0
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.
libcasio/lib/link/seven/datastream.c

368 lines
10 KiB
C

/* ****************************************************************************
* link/seven/datastream.c -- data exchanging stream.
* Copyright (C) 2016-2017 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
*
* This file is part of libcasio.
* libcasio 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.
*
* libcasio 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 libcasio; if not, see <http://www.gnu.org/licenses/>.
*
* This stream is useful when you want to encode and send on the fly, without
* having to use temporary files or god knows what else.
* ************************************************************************* */
#include "data.h"
#include "../usage/usage.h"
#define BUFSIZE CASIO_SEVEN_MAX_RAWDATA_SIZE
/* Cookie structure. */
typedef struct {
int _faulty, _read;
casio_link_t *_link;
casio_link_progress_t *_disp;
void *_disp_cookie;
unsigned int _id, _total;
unsigned int _lastsize; /* last packet size */
/* buffer management */
size_t _pos; /* position in the buffer */
unsigned char _reserved[8];
unsigned char _current[BUFSIZE];
} seven_data_cookie_t;
/* ---
* Callbacks.
* --- */
/**
* casio_seven_data_read:
* Read data from the calculator, using Protocol 7.00 data flow.
*
* @arg cookie the cookie.
* @arg data the data to read.
* @arg size the size of the data to read.
* @arg timeout the timeout (ignored).
* @return the error code (0 if ok).
*/
CASIO_LOCAL int casio_seven_data_read(seven_data_cookie_t *cookie,
unsigned char *data, size_t size, unsigned int timeout)
{
int err; size_t tocopy;
casio_link_t *handle = cookie->_link;
unsigned int lastsize;
(void)timeout;
/* Check if the stream is faulty. */
if (cookie->_faulty)
return (casio_error_noread);
/* Empty the current buffer. */
tocopy = cookie->_lastsize;
if (size < tocopy) tocopy = size;
if (tocopy) {
memcpy(data, &cookie->_current[cookie->_pos], tocopy);
cookie->_pos += tocopy;
data += tocopy; size -= tocopy;
if (!size) return (0);
}
/* Check if we have already finished. */
if (cookie->_total && cookie->_id == cookie->_total)
return (casio_error_eof);
/* Receive packets. */
while (size) {
/* Send the ack and get the answer. */
err = casio_seven_send_ack(handle, 1);
if (err) goto fail;
if (response.casio_seven_packet_type != casio_seven_type_data) {
msg((ll_error, "Packet wasn't a data packet, wtf?"));
err = casio_error_unknown;
goto fail;
}
/* Check the total. */
if (!cookie->_total)
cookie->_total = response.casio_seven_packet_total;
else if (cookie->_total != response.casio_seven_packet_total) {
msg((ll_error,
"Packet total did not correspond to the cached one...?"));
err = casio_error_unknown;
goto fail;
}
/* Check the ID. */
cookie->_id++;
if (cookie->_id != response.casio_seven_packet_id) {
msg((ll_error, "Non-consecutive data packets."));
err = casio_error_unknown;
goto fail;
}
/* Increment and copy. */
lastsize = response.casio_seven_packet_data_size;
if (size >= lastsize) {
memcpy(data, response.casio_seven_packet_data, lastsize);
data += lastsize; size -= lastsize;
continue;
}
/* Copy to the data, keep the rest! */
cookie->_lastsize = lastsize;
memcpy(data, response.casio_seven_packet_data, size);
memcpy(&cookie->_current[size],
&response.casio_seven_packet_data[size], lastsize - size);
return (0);
}
return (0);
fail:
/* XXX: tell the distant device we have a problem? */
cookie->_faulty = 1;
return (err);
}
/**
* casio_seven_data_write:
* Send data to the calculator, using Protocol 7.00 data flow.
*
* @arg cookie the cookie.
* @arg data the data to write.
* @arg size the size of the data to write.
* @arg timeout the timeout (ignored).
* @return the error code (0 if ok).
*/
CASIO_LOCAL int casio_seven_data_write(seven_data_cookie_t *cookie,
const unsigned char *data, size_t size, unsigned int timeout)
{
int err; size_t tocopy, lastlimit;
casio_link_t *handle = cookie->_link;
(void)timeout;
/* Check if the stream is faulty, or if we have already finished. */
if (cookie->_faulty)
return (casio_error_nowrite);
if (cookie->_id > cookie->_total)
return (casio_error_eof);
/* Current packet management. */
if (cookie->_pos || size < BUFSIZE) {
/* Fill the current packet. */
tocopy = size;
if (cookie->_pos + tocopy < cookie->_pos /* overflow */
|| cookie->_pos + tocopy > BUFSIZE)
tocopy = BUFSIZE - 1 - cookie->_pos;
memcpy(cookie->_current, data, tocopy);
data += tocopy; size -= tocopy;
cookie->_pos += tocopy;
/* Check if the current packet is full. */
if (cookie->_id == cookie->_total) {
if (cookie->_pos < cookie->_lastsize)
return (0);
} else if (cookie->_pos < BUFSIZE - 1)
return (0);
/* Send the packet. */
if (cookie->_id == 1 && cookie->_disp)
(*cookie->_disp)(cookie->_disp_cookie, 1, 0);
err = casio_seven_send_quick_data_packet(handle, cookie->_total,
cookie->_id, &cookie->_reserved, cookie->_pos, 1);
if (err) goto fail;
if (cookie->_disp) (*cookie->_disp)(cookie->_disp_cookie,
cookie->_id, cookie->_total);
cookie->_id++; cookie->_pos = 0;
}
/* Intermediate packets. */
while (cookie->_id < cookie->_total && size >= BUFSIZE) {
memcpy(cookie->_current, data, BUFSIZE);
err = casio_seven_send_quick_data_packet(handle,
cookie->_total, cookie->_id, &cookie->_reserved, BUFSIZE, 1);
if (err) goto fail;
if (cookie->_disp) (*cookie->_disp)(cookie->_disp_cookie,
cookie->_id, cookie->_total);
data += BUFSIZE; size -= BUFSIZE; cookie->_id++;
}
/* Copy the last bytes of the call. */
lastlimit = cookie->_id == cookie->_total ? cookie->_lastsize : BUFSIZE;
tocopy = lastlimit; if (tocopy > size) tocopy = size;
memcpy(cookie->_current, data, tocopy);
cookie->_pos = tocopy; size -= tocopy;
/* Send the last packet if required. */
if (tocopy == lastlimit) {
err = casio_seven_send_quick_data_packet(handle,
cookie->_total, cookie->_id, &cookie->_reserved, cookie->_pos, 1);
if (err) goto fail;
if (cookie->_disp) (*cookie->_disp)(cookie->_disp_cookie,
cookie->_id, cookie->_total);
cookie->_id++; cookie->_pos = 0;
}
/* Check if there are still some bytes left
* (bytes after the last packet, means the announced size
* was too small). */
if (size) return (casio_error_nowrite);
/* Everything went well! :) */
return (0);
fail:
/* XXX: tell the distant device we have a problem? */
cookie->_faulty = 1;
return (err);
}
/**
* casio_seven_data_close:
* Close the data stream.
*
* @arg cookie the cookie.
* @return the error code (0 if ok).
*/
CASIO_LOCAL int casio_seven_data_close(seven_data_cookie_t *cookie)
{
int err = 0; casio_link_t *handle = cookie->_link;
/* Check if there is some data left to receive. */
if (cookie->_read) {
if (!cookie->_total) {
err = casio_seven_send_ack(handle, 1);
if (err) goto fail;
cookie->_id = response.casio_seven_packet_id;
cookie->_total = response.casio_seven_packet_total;
if (cookie->_id != 1) { err = casio_error_unknown; goto fail; }
}
while (cookie->_id < cookie->_total) {
err = casio_seven_send_ack(handle, 1);
if (err) goto fail;
if (cookie->_id + 1 != response.casio_seven_packet_id
|| cookie->_total != response.casio_seven_packet_total) {
err = casio_error_unknown;
goto fail;
}
cookie->_id++;
}
}
/* Check if there is some data left to send. */
if (!cookie->_read && cookie->_id <= cookie->_total) {
size_t left; unsigned char zeroes[BUFSIZE];
/* Intermediate packets. */
left = (cookie->_total - cookie->_id - 1) * BUFSIZE;
/* Current and last packet. */
if (cookie->_id == cookie->_total)
left += cookie->_lastsize - cookie->_pos; /* current & end */
else {
left += BUFSIZE - cookie->_pos; /* current */
left += cookie->_lastsize; /* end */
}
/* Send the zeroes.
* If the stream gets faulty in the middle of this, fuck it and
* go directly to the end. */
memset(zeroes, 0, BUFSIZE);
for (; left >= BUFSIZE; left -= BUFSIZE) {
if ((err = casio_seven_data_write(cookie, zeroes, BUFSIZE, 0)))
goto fail;
}
err = casio_seven_data_write(cookie, zeroes, left, 0);
}
err = 0;
fail:
casio_free(cookie);
return (err);
}
/* ---
* Opening functions.
* --- */
CASIO_LOCAL const casio_streamfuncs_t seven_data_callbacks = {
(casio_stream_close_t *)casio_seven_data_close,
(casio_stream_read_t *)casio_seven_data_read,
(casio_stream_write_t *)casio_seven_data_write,
NULL, NULL, NULL, NULL, NULL
};
/**
* casio_seven_open_data_stream:
* Open a data stream.
*
* @arg stream the stream to make.
* @arg link the link with which to send the data.
* @arg size the total size of the data to transmit.
* @arg disp the display callback.
* @arg dcookie the display callback cookie.
* @return the error (0 if ok).
*/
int CASIO_EXPORT casio_seven_open_data_stream(casio_stream_t **stream,
casio_link_t *link, casio_off_t size, casio_link_progress_t *disp,
void *dcookie)
{
seven_data_cookie_t *cookie = NULL;
casio_openmode_t mode;
/* make checks */
chk_handle(link);
chk_seven(link);
/* make the cookie */
cookie = casio_alloc(1, sizeof(seven_data_cookie_t));
if (!cookie) return (casio_error_alloc);
/* initialize the cookie and mode */
cookie->_faulty = 0;
cookie->_link = link;
cookie->_pos = 0;
cookie->_disp = disp;
cookie->_disp_cookie = dcookie;
if (link->casio_link_flags & casio_linkflag_active) {
msg((ll_info, "The data stream is a write one."));
mode = CASIO_OPENMODE_WRITE;
cookie->_read = 0;
cookie->_id = 1;
cookie->_total = (unsigned int)(size / BUFSIZE) + !!cookie->_lastsize;
cookie->_lastsize = (unsigned int)(size % BUFSIZE);
if (!cookie->_lastsize) cookie->_lastsize = BUFSIZE;
} else {
msg((ll_info, "The data stream is a read one."));
mode = CASIO_OPENMODE_READ;
cookie->_read = 1;
cookie->_id = 0;
cookie->_total = 0;
cookie->_lastsize = 0;
}
/* initialize the stream */
return (casio_open_stream(stream, mode, cookie, &seven_data_callbacks, 0));
}