From c87800ab1e8c6201faa883085d8caaa9b0be221b Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Fri, 6 Dec 2024 13:08:57 +0000 Subject: [PATCH] Move serial cache from Canon to port library I've done this as one of changes in the attempt to ensure re-entrancy, but I figured it's standalone enough to deserve a separate review. This moves the serial cache (the one that reads a larger chunk to reduce overhead from reading a few bytes at a time) from canon.c to the generic serial port library. The actual cache now lives in the `_GPPortPrivateLibrary` structure instead of static variables, ensuring that it can't be accidentally modified from competing threads. Additionally, by virtue of being in the generic serial port implementation, the cache logic can now benefit all cameras that communicate over the serial port instead of just a single function in Canon driver. Note that I don't have any ability to actually test cameras over the serial port so would appreciate another pair of eyes. --- camlibs/canon/serial.c | 24 +++--------------------- libgphoto2_port/serial/unix.c | 29 +++++++++++++++++++++++++---- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/camlibs/canon/serial.c b/camlibs/canon/serial.c index fac3c9b698..55a9b79a65 100644 --- a/camlibs/canon/serial.c +++ b/camlibs/canon/serial.c @@ -219,28 +219,10 @@ serial_set_timeout (GPPort *gdev, int to) static int canon_serial_get_byte (GPPort *gdev) { - static unsigned char cache[512]; - static unsigned char *cachep = cache; - static unsigned char *cachee = cache; - int recv; - - /* if still data in cache, get it */ - if (cachep < cachee) { - return (int) *cachep++; - } - - recv = gp_port_read (gdev, (char *)cache, 1); - if (recv < 0) /* An error occurred */ + char byte; + if (gp_port_read (gdev, &byte, 1) < 0) /* An error occurred */ return -1; - - cachep = cache; - cachee = cache + recv; - - if (recv) { - return (int) *cachep++; - } - - return -1; + return byte; } /* ------------------------- Frame-level processing ------------------------- */ diff --git a/libgphoto2_port/serial/unix.c b/libgphoto2_port/serial/unix.c index baddd94c64..720a56bdff 100644 --- a/libgphoto2_port/serial/unix.c +++ b/libgphoto2_port/serial/unix.c @@ -185,6 +185,11 @@ struct _GPPortPrivateLibrary { int fd; /* Device handle */ int baudrate; /* Current speed */ int parity; /* Current parity */ + + /* Internal cache for faster reading */ + char cache[512]; + const char *cachep; + const char *cachee; }; static int gp_port_serial_check_speed (GPPort *dev); @@ -511,7 +516,7 @@ gp_port_serial_read (GPPort *dev, char *bytes, int size) FD_ZERO (&readfs); FD_SET (dev->pl->fd, &readfs); - while (readen < size) { + while (size > 0) { /* Set timeout value within input loop */ timeout.tv_usec = (dev->timeout % 1000) * 1000; @@ -558,14 +563,30 @@ gp_port_serial_read (GPPort *dev, char *bytes, int size) /* Ok, we read 1 byte and it is 0xff */ /* FALLTHROUGH */ } - } else { - /* Just read the bytes */ - now = read (dev->pl->fd, bytes, size - readen); + } else if (dev->pl->cachep == dev->pl->cachee && size >= sizeof(dev->pl->cache)) { + /* We're trying to read a chunk larger than the cache and the cache is empty. + * In this case, skip the cache entirely and read as much as we can directly into the destination. */ + now = read (dev->pl->fd, bytes, size); if (now < 0) return GP_ERROR_IO_READ; + } else { + if (dev->pl->cachep == dev->pl->cachee) { + /* We're reading only a few bytes and the cache is empty; fill it up. */ + now = read (dev->pl->fd, dev->pl->cache, sizeof(dev->pl->cache)); + if (now < 0) + return GP_ERROR_IO_READ; + /* Reset cache pointers */ + dev->pl->cachep = dev->pl->cache; + dev->pl->cachee = dev->pl->cache + now; + } + /* read up to the required chunk size from cache */ + now = MIN(size, dev->pl->cachee - dev->pl->cachep); + memcpy(bytes, dev->pl->cachep, now); + dev->pl->cachep += now; } bytes += now; readen += now; + size -= readen; } return readen;