fxsdk/libfxlink/defs.c
Lephenixnoir 85d7fcf9e9
libfxlink: fix race condition leading to lost messages
Basically if the calculator sends two messages in a row, it is possible
for a single libusb_handle_events() to get both. And the comm structure
wasn't designed for that, because it could buffer only one message at a
time, which the user needed to read after event handling.

The comm structure now has a 16-message buffer, which should be more
than enough for any single event handling loop. On the user level this
has implications in that fxlink_device_finish_bulk_IN() must be called
*in a loop* after each event handling cycle.

Reported in https://git.planet-casio.com/Lephenixnoir/gint/pulls/27
2024-03-24 19:25:35 +01:00

172 lines
4.5 KiB
C

//---------------------------------------------------------------------------//
// ==>/[_]\ fxlink: A community communication tool for CASIO calculators. //
// |:::| Made by Lephe' as part of the fxSDK. //
// \___/ License: MIT <https://opensource.org/licenses/MIT> //
//---------------------------------------------------------------------------//
#include <fxlink/defs.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
char const *fmt_to_ANSI(int format)
{
static char buf[64];
int n = 0;
strcpy(buf, "\e[0m");
n += 4;
int FG = fmt_FG(format);
int BG = fmt_BG(format);
if(FG != 0)
n += sprintf(buf+n, "\e[%dm", 30 + FG - 1);
if(BG != 0)
n += sprintf(buf+n, "\e[%dm", 40 + BG - 1);
if(fmt_BOLD(format))
strcpy(buf+n, "\e[1m"), n += 4;
if(fmt_DIM(format))
strcpy(buf+n, "\e[2m"), n += 4;
if(fmt_ITALIC(format))
strcpy(buf+n, "\e[3m"), n += 4;
return buf;
}
char *fxlink_gen_file_name(char const *path, char const *name,
char const *suffix)
{
char *filename = NULL;
int counter = 1;
time_t time_raw;
struct tm time_bd;
time(&time_raw);
localtime_r(&time_raw, &time_bd);
while(1) {
asprintf(&filename, "%s/fxlink-%.16s-%04d.%02d.%02d-%02dh%02d-%d%s",
path, name, time_bd.tm_year + 1900, time_bd.tm_mon + 1,
time_bd.tm_mday, time_bd.tm_hour, time_bd.tm_min, counter, suffix);
if(!filename)
continue;
/* Try to find a name for a file that doesn't exist */
if(access(filename, F_OK) == -1)
break;
free(filename);
counter++;
}
return filename;
}
int fxlink_multipoll(int timeout, struct pollfd *fds1, int count1, ...)
{
/* Convenience macro to iterate on file descriptor arrays */
#define FOREACH_FD_ARRAY(FDS, COUNT, COUNT_ACC, BODY) do { \
struct pollfd *FDS; int COUNT, COUNT_ACC; va_list args; \
va_start(args, count1); \
for(FDS = fds1, COUNT = count1, COUNT_ACC = 0; FDS; \
COUNT_ACC += COUNT, \
FDS = va_arg(args, struct pollfd *), \
COUNT = va_arg(args, int)) { BODY } \
va_end(args); } while(0)
/* Determine total number of file descriptors to watch */
int total = 0;
FOREACH_FD_ARRAY(fds, count, count_acc, {
total += count;
});
struct pollfd *concat = malloc(total * sizeof *concat);
if(!concat)
return -ENOMEM;
/* Copy from individual arrays to full array */
FOREACH_FD_ARRAY(fds, count, count_acc, {
memcpy(concat + count_acc, fds, count * sizeof *fds);
});
int rc = poll(concat, total, timeout);
/* Copy back from full array to individual arrays */
FOREACH_FD_ARRAY(fds, count, count_acc, {
memcpy(fds, concat + count_acc, count * sizeof *fds);
});
free(concat);
return rc;
}
char const *fxlink_size_string(int bytes)
{
static char str[32];
if(bytes > 1000000)
sprintf(str, "%d.%d MB", bytes / 1000000, (bytes % 1000000) / 100000);
else if(bytes > 1000)
sprintf(str, "%d.%d kB", bytes / 1000, (bytes % 1000) / 100);
else
sprintf(str, "%d B", bytes);
return str;
}
delay_t delay_none(void)
{
return 0;
}
delay_t delay_seconds(int seconds)
{
return seconds * 4;
}
delay_t delay_infinite(void)
{
return -1;
}
bool delay_cycle(delay_t *delay)
{
if(*delay == 0) return true;
struct timespec spec = { .tv_sec=0, .tv_nsec=250000000 };
int rc;
/* Account for interrupts in the nanosleep(2) call */
struct timespec req = spec;
do rc = nanosleep(&req, &req);
while(rc == -1 && errno == EINTR);
if(*delay > 0) (*delay)--;
return false;
}
void fxlink_hexdump(char const *data, int size, FILE *fp)
{
for(int read = 0; read < size; read += 16) {
for(int i = 0; i < 16; i++) {
if(read + i < size) {
int byte = data[read + i] & 0xff;
fprintf(fp, " %02x", byte);
}
else {
fprintf(fp, " ");
}
}
fprintf(fp, " | ");
for(int i = 0; i < 16 && read + i < size; i++) {
int byte = data[read + i] & 0xff;
fprintf(fp, "%c", isprint(byte) ? byte : '.');
}
fprintf(fp, "\n");
}
}