darwin: add get_running_version helper

This helper returns an integer representation of the currently running
OS from 100000 (10.0) on. This allows the Darwin backend to make
decisions base on the running version without using the
__builtin_available() feature. This builtin is only available in Clang
and not gcc. For newer versions of macOS the helper uses the
kern.osproductversion sysctl and falls back to kern.osrelease and a
mapping of Darwin -> OS X/macOS versions.

Signed-off-by: Nathan Hjelm <hjelmn@google.com>
This commit is contained in:
Nathan Hjelm
2023-04-03 19:05:43 -06:00
committed by Nathan Hjelm
parent 43c6fe0b59
commit 33e92b6c49
3 changed files with 72 additions and 13 deletions

View File

@@ -170,6 +170,67 @@ static enum libusb_error darwin_to_libusb (IOReturn result) {
}
}
uint32_t get_running_version(void) {
int ret;
#if !defined(TARGET_OS_OSX) || TARGET_OS_OSX == 1
char os_version_string[64] = {'\0'};;
size_t os_version_string_len = sizeof(os_version_string) - 1;
/* newer versions of macOS provide a sysctl for the OS version but this is not useful for iOS without
* code detecting this is iOS and a mapping from iOS -> macOS version. it is still useful to have since
* it provides the exact macOS version instead of the approximate version (as below). */
ret = sysctlbyname("kern.osproductversion", os_version_string, &os_version_string_len, NULL, 0);
if (ret == 0) {
int major = 10, minor = 0, patch = 0;
ret = sscanf(os_version_string, "%i.%i.%i", &major, &minor, &patch);
if (ret < 2) {
usbi_err (NULL, "could not determine the running OS version, assuming 10.0, kern.osproductversion=%s", os_version_string);
return 100000;
}
return (major * 10000) + (minor * 100) + patch;
}
#endif
char os_release_string[64] = {'\0'};
size_t os_release_string_len = sizeof(os_release_string) - 1;
/* if the version can not be detected libusb assumes 10.0 so ignore any error here */
ret = sysctlbyname("kern.osrelease", os_release_string, &os_release_string_len, NULL, 0);
if (ret != 0) {
usbi_err (NULL, "could not read kern.osrelease, errno=", errno);
return 100000;
}
int darwin_major = 1, darwin_minor = 0;
ret = sscanf(os_release_string, "%i.%i", &darwin_major, &darwin_minor);
if (ret < 1) {
usbi_err (NULL, "could not determine the running Darwin version, assuming 1.3 (OS X 10.0), kern.osrelease=%s", os_release_string);
return 100000;
}
int major = 10, minor = 0, patch = 0;
if (1 == darwin_major && darwin_minor < 4) {
/* 10.0.x */
} else if (darwin_major < 6) {
/* assume 10.1 for anything in this range */
minor = 1;
} else if (darwin_major < 20) {
/* from macOS 10.2 through 10.15 the minor version can be calculated from the darwin_major by subtracting 4 and
* the patch level almost always matches darwin_minor. when the darwin_minor does not match the OS X patch level
* it is usually because Apple did not change it in a particular point release. when darwin_minor is changed it
* always matches the OS X/macOS patch level. */
minor = darwin_major - 4;
patch = darwin_minor;
} else {
/* unlikely to be used as kern.osproductversion is available from 10.10 on */
major = darwin_major - 9;
minor = darwin_minor;
/* ignore the patch level in this range */
}
return (major * 10000) + (minor * 100) + patch;
}
/* this function must be called with the darwin_cached_devices_mutex held */
static void darwin_deref_cached_device(struct darwin_cached_device *cached_dev) {
cached_dev->refcount--;
@@ -1806,7 +1867,7 @@ static int darwin_reenumerate_device (struct libusb_device_handle *dev_handle, b
}
/* if we need to release capture */
if (HAS_CAPTURE_DEVICE()) {
if (get_running_version() >= 101000) {
if (capture) {
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
options |= kUSBReEnumerateCaptureDeviceMask;
@@ -2555,7 +2616,7 @@ static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle,
enum libusb_error err;
struct libusb_context *ctx = HANDLE_CTX (dev_handle);
if (HAS_CAPTURE_DEVICE()) {
if (get_running_version() >= 101000) {
} else {
return LIBUSB_ERROR_NOT_SUPPORTED;
}
@@ -2599,7 +2660,7 @@ static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle,
UNUSED(interface);
struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev);
if (HAS_CAPTURE_DEVICE()) {
if (get_running_version() >= 101000) {
} else {
return LIBUSB_ERROR_NOT_SUPPORTED;
}

View File

@@ -160,15 +160,13 @@
#define IO_OBJECT_NULL ((io_object_t) 0)
#endif
/* Testing availability */
#ifndef __has_builtin
#define __has_builtin(x) 0 // Compatibility with non-clang compilers.
#endif
#if __has_builtin(__builtin_available)
#define HAS_CAPTURE_DEVICE() __builtin_available(macOS 10.10, *)
#else
#define HAS_CAPTURE_DEVICE() 0
#endif
/* returns the current macOS version in a format similar to the
* MAC_OS_X_VERSION_MIN_REQUIRED macro.
* Examples:
* 10.1.5 -> 100105
* 13.3.0 -> 130300
*/
uint32_t get_running_version(void);
typedef IOCFPlugInInterface *io_cf_plugin_ref_t;
typedef IONotificationPortRef io_notification_port_t;

View File

@@ -1 +1 @@
#define LIBUSB_NANO 11838
#define LIBUSB_NANO 11839