Skip to content

Commit

Permalink
Move serial cache from Canon to port library
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
RReverser committed Dec 6, 2024
1 parent 1584f53 commit c87800a
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 25 deletions.
24 changes: 3 additions & 21 deletions camlibs/canon/serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 ------------------------- */
Expand Down
29 changes: 25 additions & 4 deletions libgphoto2_port/serial/unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit c87800a

Please sign in to comment.