mirror of
https://github.com/tio/tio.git
synced 2026-05-01 14:57:59 +02:00
- Implemented getPropertyString(), getDeviceLocation(), tty_search_for_serial_devices()
for MacOS - Added error handling and memory management - Improved code readability and consistency - Updated coding style to match project conventions - Added robust error checking for CoreFoundation property retrieval - Implemented more defensive memory allocation and type checking - Switched to using callout device key for more reliable device discovery - Added single-line block bracing consistent with project style - Improved comments and code formatting - Used `kIOCalloutDeviceKey` instead of `kIODialinDeviceKey` for device path retrieval - Enhanced type checking for CoreFoundation objects - Simplified memory management and error handling - Added additional logging and error reporting - Verified functionality on MacOS 10.11 and 10.15. Tested with ESP32-P4 and ESP32-BOX Resolves potential device discovery and memory management issues in the MacOS serial device detection code.
This commit is contained in:
parent
437881f0ed
commit
03ef931fb2
1 changed files with 265 additions and 4 deletions
263
src/tty.c
263
src/tty.c
|
|
@ -22,6 +22,15 @@
|
|||
#if defined(__linux__)
|
||||
#include <linux/serial.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) || defined(__MACH__)
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <IOKit/IOBSD.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/serial/IOSerialKeys.h>
|
||||
#include <IOKit/usb/IOUSBLib.h>
|
||||
#endif
|
||||
|
||||
#include "version.h"
|
||||
#include "config.h"
|
||||
#include <stdarg.h>
|
||||
|
|
@ -1841,6 +1850,246 @@ GList *tty_search_for_serial_devices(void)
|
|||
return device_list;
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__) || defined(__MACH__)
|
||||
|
||||
char *getPropertyString(io_object_t device, CFStringRef property)
|
||||
{
|
||||
/* Validate inputs */
|
||||
if (device == IO_OBJECT_NULL || property == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Attempt to get property */
|
||||
CFTypeRef valueRef = IORegistryEntryCreateCFProperty(
|
||||
device, property, kCFAllocatorDefault, 0);
|
||||
if (!valueRef)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Ensure it's a CFString */
|
||||
if (CFGetTypeID(valueRef) != CFStringGetTypeID())
|
||||
{
|
||||
CFRelease(valueRef);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Convert to C string */
|
||||
CFIndex length = CFStringGetLength(valueRef);
|
||||
CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
|
||||
char *result = malloc(maxSize);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
CFRelease(valueRef);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool converted = CFStringGetCString(
|
||||
(CFStringRef)valueRef,
|
||||
result,
|
||||
maxSize,
|
||||
kCFStringEncodingUTF8
|
||||
);
|
||||
|
||||
CFRelease(valueRef);
|
||||
|
||||
if (!converted)
|
||||
{
|
||||
free(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
char *getDeviceLocation(io_object_t device)
|
||||
{
|
||||
/* Validate device */
|
||||
if (device == IO_OBJECT_NULL)
|
||||
{
|
||||
return strdup("Invalid Device");
|
||||
}
|
||||
|
||||
/* Attempt to get location */
|
||||
io_string_t location = {0};
|
||||
kern_return_t result = IORegistryEntryGetLocationInPlane(
|
||||
device, kIOServicePlane, location);
|
||||
|
||||
if (result != KERN_SUCCESS)
|
||||
{
|
||||
return strdup("Unknown Location");
|
||||
}
|
||||
|
||||
/* Safely copy location */
|
||||
size_t len = strnlen(location, sizeof(io_string_t));
|
||||
char *trimmed_location = calloc(1, len + 1);
|
||||
|
||||
if (!trimmed_location)
|
||||
{
|
||||
return strdup("Memory Error");
|
||||
}
|
||||
|
||||
memcpy(trimmed_location, location, len);
|
||||
return trimmed_location;
|
||||
}
|
||||
|
||||
// for __APPLE__
|
||||
GList *tty_search_for_serial_devices(void)
|
||||
{
|
||||
GList *device_list = NULL;
|
||||
io_iterator_t iter = IO_OBJECT_NULL;
|
||||
CFMutableDictionaryRef matchingDict = NULL;
|
||||
listing_device_name_length_max = 0;
|
||||
|
||||
/* Create matching dictionary for serial devices */
|
||||
if (!(matchingDict = IOServiceMatching(kIOSerialBSDServiceValue)))
|
||||
{
|
||||
tio_error_print("Failed to create matching dictionary for serial devices");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get matching services */
|
||||
kern_return_t kernResult = IOServiceGetMatchingServices(
|
||||
kIOMainPortDefault, matchingDict, &iter);
|
||||
matchingDict = NULL; /* Dictionary ownership transferred */
|
||||
|
||||
if (kernResult != KERN_SUCCESS)
|
||||
{
|
||||
tio_error_print("IOServiceGetMatchingServices failed: %d", kernResult);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Defensive check for iterator */
|
||||
if (iter == IO_OBJECT_NULL)
|
||||
{
|
||||
tio_error_print("Invalid device iterator");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Iterate through serial devices and collect information */
|
||||
for (io_object_t device; (device = IOIteratorNext(iter));)
|
||||
{
|
||||
char *devicePath = NULL, *locationID = NULL;
|
||||
char *productName = NULL, *vendorName = NULL;
|
||||
char tid[5] = {0};
|
||||
double uptime = 0.0;
|
||||
|
||||
/* Get device path - key determines if we get tty. or cu. */
|
||||
//if (!(devicePath = getPropertyString(device, CFSTR(kIODialinDeviceKey))))
|
||||
if (!(devicePath = getPropertyString(device, CFSTR(kIOCalloutDeviceKey))))
|
||||
{
|
||||
IOObjectRelease(device);
|
||||
continue; /* Skip devices without a path */
|
||||
}
|
||||
|
||||
/* Update length of longest device name string */
|
||||
listing_device_name_length_max =
|
||||
strlen(devicePath) > listing_device_name_length_max
|
||||
? strlen(devicePath)
|
||||
: listing_device_name_length_max;
|
||||
|
||||
/* Calculate uptime */
|
||||
uptime = get_current_time() - fs_get_creation_time(devicePath);
|
||||
|
||||
/* Find USB device (if applicable) */
|
||||
io_object_t usbDevice = IO_OBJECT_NULL;
|
||||
kern_return_t usbResult = IORegistryEntryGetParentEntry(
|
||||
device, kIOServicePlane, &usbDevice);
|
||||
|
||||
/* Traverse up the device tree to find a USB device */
|
||||
while (usbResult == KERN_SUCCESS &&
|
||||
!IOObjectConformsTo(usbDevice, "IOUSBDevice"))
|
||||
{
|
||||
io_object_t oldUsbDevice = usbDevice;
|
||||
usbResult = IORegistryEntryGetParentEntry(
|
||||
usbDevice, kIOServicePlane, &usbDevice);
|
||||
IOObjectRelease(oldUsbDevice);
|
||||
}
|
||||
|
||||
/* If we found a USB device */
|
||||
if (usbResult == KERN_SUCCESS)
|
||||
{
|
||||
locationID = getDeviceLocation(usbDevice);
|
||||
|
||||
unsigned long hash2 = djb2_hash((const unsigned char *)(locationID ?: ""));
|
||||
base62_encode(hash2, tid);
|
||||
|
||||
/* Get product and vendor names */
|
||||
productName = getPropertyString(usbDevice, CFSTR("USB Product Name"));
|
||||
vendorName = getPropertyString(usbDevice, CFSTR("USB Vendor Name"));
|
||||
|
||||
IOObjectRelease(usbDevice);
|
||||
}
|
||||
|
||||
/* Create device structure */
|
||||
device_t *device_info = g_new0(device_t, 1);
|
||||
if (!device_info)
|
||||
{
|
||||
tio_error_print("Memory allocation failed for device_info");
|
||||
free(devicePath);
|
||||
free(locationID);
|
||||
free(productName);
|
||||
free(vendorName);
|
||||
IOObjectRelease(device);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Populate device info */
|
||||
*device_info = (device_t) {
|
||||
.path = devicePath,
|
||||
.tid = g_strdup(tid),
|
||||
.uptime = uptime,
|
||||
.driver = g_strdup(vendorName),
|
||||
.description = g_strdup(productName ?: vendorName ?: "")
|
||||
};
|
||||
|
||||
/* Add to device list */
|
||||
device_list = g_list_append(device_list, device_info);
|
||||
|
||||
/* Clean up */
|
||||
free(locationID);
|
||||
free(productName);
|
||||
free(vendorName);
|
||||
IOObjectRelease(device);
|
||||
}
|
||||
|
||||
/* Clean up iterator */
|
||||
IOObjectRelease(iter);
|
||||
|
||||
/* Check if device list is empty */
|
||||
if (!device_list)
|
||||
{
|
||||
tio_error_print("No serial devices found");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Sort device list by uptime */
|
||||
device_list = g_list_sort(device_list, compare_uptime);
|
||||
|
||||
/* Print header for device listing */
|
||||
print_padded("Device", listing_device_name_length_max, ' ');
|
||||
printf(" TID Uptime [s] Driver Description\n");
|
||||
print_padded("", listing_device_name_length_max, '-');
|
||||
printf(" ---- -------------- ---------------- --------------------------\n");
|
||||
|
||||
/* Print sorted device list */
|
||||
for (GList *l = device_list; l; l = l->next)
|
||||
{
|
||||
device_t *dev = l->data;
|
||||
printf("%-*s %-4s %14.3f %-16s %s\n",
|
||||
(int)listing_device_name_length_max, dev->path,
|
||||
dev->tid ?: "",
|
||||
dev->uptime,
|
||||
dev->driver ?: "",
|
||||
dev->description ?: "");
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
return device_list;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
GList *tty_search_for_serial_devices(void)
|
||||
|
|
@ -2121,8 +2370,21 @@ void tty_wait_for_device(void)
|
|||
}
|
||||
else if (status == -1)
|
||||
{
|
||||
#if defined(__CYGWIN__)
|
||||
// Happens when port unpluged
|
||||
if (errno == EACCES)
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
if (errno == EBADF)
|
||||
{
|
||||
break; // tty_disconnect() will be naturally triggered by atexit()
|
||||
}
|
||||
#else
|
||||
tio_error_printf("select() failed (%s)", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2761,4 +3023,3 @@ error_read:
|
|||
error_open:
|
||||
return TIO_ERROR;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue