Add new ways to manage serial devices

* Rename --list-devices to --list

 * Rename --no-autoconnect to --no-reconnect

 * Switch -l and -L options

   * -l now lists available serial devices

   * -L enables log to file

 * Add option --auto-connect <strategy>

   * Supported strategies:

     * "new" - Waits to connect first new appearing serial device

     * "latest" - Connects to latest registered serial device

     * "direct" - Connect directly to specified serial device (default)

 * Add options to exclude serial devices from auto connect strategy by
   pattern

   * Supported exclude options:

     * --exclude-devices <pattern>

       Example: '--exclude-devices "/dev/ttyUSB2,/dev/ttyS?"'

     * --exclude-drivers <pattern>

       Example: '--exclude-drivers "cdc_acm"'

     * --exclude-tids <pattern>

       Example: '--exclude-tids "yW07,bCC2"'

     * Patterns support '*' and '?'

 * Connect to same port/device combination via unique topology ID (TID)

   * Topology ID is a 4 digit base62 encoded hash of a device topology
     string coming from the Linux kernel. This means that whenever you
     plug in the same e.g. USB serial port device to the same USB hub
     port connected via the exact same hub topology all the way to your
     computer, you will get the same unique TID.

   * Useful for stable reconnections when serial device has no serial
     device by ID

   * For now, only tested on Linux.

 * Reworked and improved listing of serial devices to show serial devices:

   * By device

     * Including TID, uptime, driver, and description.

     * Sorted by uptime (newest device listed last)

   * By unique topology ID

   * By ID

   * By path

 * Add script interface 'list = tty_search()' for searching for serial
   devices.
This commit is contained in:
Martin Lund 2024-04-26 20:34:17 +02:00
parent ae76f8f58d
commit d19ba1c492
22 changed files with 1468 additions and 150 deletions

View file

@ -20,7 +20,10 @@
*/
#include "config.h"
#include <ctype.h>
#include <dirent.h>
#include <regex.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@ -29,6 +32,7 @@
#include <time.h>
#include <errno.h>
#include <sys/poll.h>
#include <termios.h>
#include "error.h"
#include "print.h"
#include "options.h"
@ -74,22 +78,6 @@ int ctrl_key_code(unsigned char key)
return -1;
}
bool fs_dir_exists(const char *path)
{
struct stat st;
if (stat(path, &st) != 0)
{
return false;
}
else if (!S_ISDIR(st.st_mode))
{
return false;
}
return true;
}
bool regex_match(const char *string, const char *pattern)
{
regex_t regex;
@ -141,3 +129,116 @@ int read_poll(int fd, void *data, size_t len, int timeout)
/* Timeout */
return ret;
}
// Function to calculate djb2 hash of string
unsigned long djb2_hash(const unsigned char *str)
{
unsigned long hash = 5381;
int c;
while ((c = *str++))
{
hash = ((hash << 5) + hash) + c; // hash * 33 + c
}
return hash;
}
// Function to encode a number to base62
char *base62_encode(unsigned long num)
{
const char base62_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
char *output = (char *) malloc(5); // 4 characters + null terminator
if (output == NULL)
{
tio_error_print("Memory allocation failed");
exit(EXIT_FAILURE);
}
for (int i = 0; i < 4; ++i)
{
output[i] = base62_chars[num % 62];
num /= 62;
}
output[4] = '\0';
return output;
}
// Function to return current time
double get_current_time(void)
{
struct timespec current_time_ts;
if (clock_gettime(CLOCK_REALTIME, &current_time_ts) == -1)
{
// Error
return -1;
}
return current_time_ts.tv_sec + current_time_ts.tv_nsec / 1e9;
}
// Function to match string with comma separated patterns which supports '*' and '?'
static bool is_match(const char *str, const char *pattern)
{
// If both string and pattern reach end, they match
if (*str == '\0' && *pattern == '\0')
{
return true;
}
// If pattern reaches end but string has characters left, no match
if (*pattern == '\0')
{
return false;
}
// If current characters match or pattern has '?', move to the next character in both
if (*str == *pattern || *pattern == '?')
{
return is_match(str + 1, pattern + 1);
}
// If current pattern character is '*', check for matches by moving string or pattern
if (*pattern == '*')
{
// '*' matches zero or more characters, so try all possibilities
// Move pattern to the next character and check if remaining pattern matches remaining string
// Move string to the next character and check if current pattern matches remaining string
return is_match(str, pattern + 1) || is_match(str + 1, pattern);
}
// No match
return false;
}
bool match_any_pattern(const char *str, const char *patterns)
{
if ((str == NULL) || (patterns == NULL))
{
return false;
}
char *patterns_copy = strdup(patterns);
if (patterns_copy == NULL)
{
tio_error_print("Memory allocation failed");
exit(EXIT_FAILURE);
}
char *token = strtok(patterns_copy, ",");
while (token != NULL)
{
if (is_match(str, token))
{
free(patterns_copy);
return true;
}
token = strtok(NULL, ",");
}
free(patterns_copy);
return false;
}