Permalink
Browse files

Automated Pairing for Mac OS X 10.8

This has been tested by me under Mac OS X 10.8, and should
work with older versions of Mac OS X as well.

The current implementation is very defensive (i.e. we wait
for "blued" to shutdown by itself instead of killing it),
which means that pairing a controller that's not known to
the system takes ~ 42 seconds (but it seems very reliable).
  • Loading branch information...
1 parent 6dc41d2 commit d0397774f12fa2b95943589a94e2fcbfdba70e6d @thp committed Sep 20, 2012
View
36 src/platform/psmove_linuxsupport.c
@@ -214,40 +214,6 @@ write_entry_to_file(const char *base, const char *filename,
return process_file_entry(base, filename, addr, entry, 1);
}
-char *
-normalize_addr(const char *addr)
-{
- int count = strlen(addr);
-
- if (count != 17) {
- printf("Invalid address: '%s'\n", addr);
- return NULL;
- }
-
- char *result = malloc(count + 1);
- int i;
-
- for (i=0; i<strlen(addr); i++) {
- if (addr[i] >= 'A' && addr[i] <= 'F' && i % 3 != 2) {
- result[i] = addr[i];
- } else if (addr[i] >= '0' && addr[i] <= '9' && i % 3 != 2) {
- result[i] = addr[i];
- } else if (addr[i] >= 'a' && addr[i] <= 'f' && i % 3 != 2) {
- result[i] = toupper(addr[i]);
- } else if ((addr[i] == ':' || addr[i] == '-') && i % 3 == 2) {
- result[i] = ':';
- } else {
- printf("Invalid character at pos %d: '%c'\n", i, addr[i]);
- free(result);
- return NULL;
- }
- }
-
- result[count] = '\0';
- return result;
-}
-
-
typedef int (*for_all_entries_func)(const char *base, const char *filename,
const char *addr, const char *entry);
@@ -272,7 +238,7 @@ linux_bluez_register_psmove(char *addr)
{
int errors = 0;
- addr = normalize_addr(addr);
+ addr = _psmove_normalize_btaddr(addr, 0, ':');
if (addr == NULL) {
printf("Cannot parse bluetooth address!\n");
return 0;
View
3 src/platform/psmove_osxsupport.h
@@ -39,6 +39,9 @@ extern "C" {
char *
macosx_get_btaddr();
+int
+macosx_blued_register_psmove(char *addr);
+
#ifdef __cplusplus
}
#endif
View
133 src/platform/psmove_osxsupport.m
@@ -27,14 +27,48 @@
* POSSIBILITY OF SUCH DAMAGE.
**/
-#include <IOBluetooth/objc/IOBluetoothHostController.h>
#include "psmove_osxsupport.h"
#include "../psmove_private.h"
+#include <IOBluetooth/objc/IOBluetoothHostController.h>
+
+/* Location for the plist file that we want to modify */
+#define OSX_BT_CONFIG_PATH "/Library/Preferences/com.apple.Bluetooth"
+
+/* Function declarations for IOBluetooth private API */
+void IOBluetoothPreferenceSetControllerPowerState(int);
+int IOBluetoothPreferenceGetControllerPowerState();
+
+#define OSXPAIR_DEBUG(msg, ...) \
+ psmove_PRINTF("PAIRING OSX", msg, ## __VA_ARGS__)
+
+int
+macosx_bluetooth_set_powered(int powered)
+{
+ // Inspired by blueutil from Frederik Seiffert <ego@frederikseiffert.de>
+ int state = IOBluetoothPreferenceGetControllerPowerState();
+ if (powered != state) {
+ OSXPAIR_DEBUG("Switching Bluetooth %s...\n", powered?"on":"off");
+ IOBluetoothPreferenceSetControllerPowerState(powered);
+
+ // Wait a bit for Bluetooth to be (de-)activated
+ usleep(1000000);
+
+ if (IOBluetoothPreferenceGetControllerPowerState() != powered) {
+ // Happened to me once while Bluetooth devices were connected
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
char *
macosx_get_btaddr()
{
- char *result;
+ const char *result;
+
+ macosx_bluetooth_set_powered(1);
IOBluetoothHostController *controller =
[IOBluetoothHostController defaultController];
@@ -48,3 +82,98 @@
return strdup(result);
}
+int
+macosx_blued_running()
+{
+ FILE *fp = popen("ps -axo comm", "r");
+ char command[1024];
+ int running = 0;
+
+ while (fgets(command, sizeof(command), fp)) {
+ /* Remove trailing newline */
+ command[strlen(command)-1] = '\0';
+
+ if (strcmp(command, "/usr/sbin/blued") == 0) {
+ running = 1;
+ }
+ }
+
+ pclose(fp);
+
+ return running;
+}
+
+int
+macosx_blued_is_paired(char *btaddr)
+{
+ FILE *fp = popen("defaults read " OSX_BT_CONFIG_PATH " HIDDevices", "r");
+ char line[1024];
+ int found = 0;
+
+ /**
+ * Example output that we need to parse:
+ *
+ * (
+ * "e0-ae-5e-00-00-00",
+ * "e0-ae-5e-aa-bb-cc",
+ * "00-06-f7-22-11-00",
+ * )
+ *
+ **/
+
+ while (fgets(line, sizeof(line), fp)) {
+ char *entry = strchr(line, '"');
+ if (entry) {
+ entry++;
+ char *delim = strchr(entry, '"');
+ if (delim) {
+ *delim = '\0';
+ if (strcmp(entry, btaddr) == 0) {
+ found = 1;
+ }
+ }
+ }
+ }
+
+ pclose(fp);
+ return found;
+}
+
+int
+macosx_blued_register_psmove(char *addr)
+{
+ char cmd[1024];
+ char *btaddr = _psmove_normalize_btaddr(addr, 1, '-');
+
+ if (macosx_blued_is_paired(btaddr)) {
+ OSXPAIR_DEBUG("Entry for %s already present.\n", btaddr);
+ return 1;
+ }
+
+ if (!macosx_bluetooth_set_powered(0)) {
+ OSXPAIR_DEBUG("Cannot shutdown Bluetooth.\n");
+ return 0;
+ }
+
+ int i = 0;
+ OSXPAIR_DEBUG("Waiting for blued shutdown (takes ca. 42s) ...\n");
+ while (macosx_blued_running()) {
+ usleep(1000000);
+ i++;
+ }
+ OSXPAIR_DEBUG("blued successfully shutdown.\n");
+
+ snprintf(cmd, sizeof(cmd), "osascript -e 'do shell script "
+ "\"defaults write " OSX_BT_CONFIG_PATH
+ " HIDDevices -array-add \\\"%s\\\"\""
+ " with administrator privileges'", btaddr);
+ OSXPAIR_DEBUG("Running: '%s'\n", cmd);
+ if (system(cmd) != 0) {
+ OSXPAIR_DEBUG("Could not run the command.");
+ }
+ macosx_bluetooth_set_powered(1);
+ free(btaddr);
+
+ return 1;
+}
+
View
85 src/psmove.c
@@ -297,9 +297,7 @@ _psmove_led_write_thread_proc(void *data)
memcpy(&leds, &(move->leds), sizeof(leds));
move->led_write_thread_write_queued = 0;
-#ifdef PSMOVE_DEBUG
long started = psmove_util_get_ticks();
-#endif
#if defined(__linux)
/* Don't write padding bytes on Linux (makes it faster) */
@@ -310,11 +308,9 @@ _psmove_led_write_thread_proc(void *data)
sizeof(leds));
#endif
-#ifdef PSMOVE_DEBUG
- fprintf(stderr, "hid_write(%d) = %ld ms\n",
+ psmove_DEBUG("hid_write(%d) = %ld ms\n",
move->id,
psmove_util_get_ticks() - started);
-#endif
pthread_yield();
} while (memcmp(&leds, &(move->leds), sizeof(leds)) != 0);
@@ -689,15 +685,10 @@ _psmove_read_btaddrs(PSMove *move, PSMove_Data_BTAddr *host, PSMove_Data_BTAddr
memcpy(*controller, btg+1, 6);
}
-#ifdef PSMOVE_DEBUG
- fprintf(stderr, "[PSMOVE] Current host: ");
- int i;
- for (i=15; i>=10; i--) {
- if (i != 15) putc(':', stderr);
- fprintf(stderr, "%02x", btg[i]);
- }
- fprintf(stderr, "\n");
-#endif
+ char *current_host = _psmove_btaddr_to_string(btg+10);
+ psmove_DEBUG("Current host: %s\n", current_host);
+ free(current_host);
+
if (host != NULL) {
memcpy(*host, btg+10, 6);
}
@@ -863,19 +854,24 @@ psmove_pair(PSMove *move)
if (!psmove_set_btaddr(move, &btaddr)) {
return PSMove_False;
}
-#ifdef PSMOVE_DEBUG
} else {
- fprintf(stderr, "[PSMOVE] Already paired.\n");
-#endif
+ psmove_DEBUG("Already paired.\n");
}
+ char *addr = psmove_get_serial(move);
+
#if defined(__linux)
/* Add entry to Bluez' bluetoothd state file */
- char *addr = psmove_get_serial(move);
linux_bluez_register_psmove(addr);
- free(addr);
#endif
+#if defined(__APPLE__)
+ /* Add entry to the com.apple.Bluetooth.plist file */
+ macosx_blued_register_psmove(addr);
+#endif
+
+ free(addr);
+
return PSMove_True;
}
@@ -899,10 +895,8 @@ psmove_pair_custom(PSMove *move, const char *btaddr_string)
if (!psmove_set_btaddr(move, &btaddr)) {
return PSMove_False;
}
-#ifdef PSMOVE_DEBUG
} else {
- fprintf(stderr, "[PSMOVE] Already paired.\n");
-#endif
+ psmove_DEBUG("Already paired.\n");
}
return PSMove_True;
@@ -1078,10 +1072,8 @@ psmove_poll(PSMove *move)
psmove_return_val_if_fail(move != NULL, 0);
-#ifdef PSMOVE_DEBUG
/* store old sequence number before reading */
int oldseq = (move->input.buttons4 & 0x0F);
-#endif
switch (move->type) {
case PSMove_HIDAPI:
@@ -1126,12 +1118,10 @@ psmove_poll(PSMove *move)
* consumers to utilize the data
**/
int seq = (move->input.buttons4 & 0x0F);
-#ifdef PSMOVE_DEBUG
if (seq != ((oldseq + 1) % 16)) {
- fprintf(stderr, "[PSMOVE] Warning: Dropped frames (seq %d -> %d)\n",
+ psmove_DEBUG("Warning: Dropped frames (seq %d -> %d)\n",
oldseq, seq);
}
-#endif
if (move->orientation_enabled) {
psmove_orientation_update(move->orientation);
@@ -1533,3 +1523,44 @@ psmove_util_get_env_string(const char *name)
return NULL;
}
+char *
+_psmove_normalize_btaddr(const char *addr, int lowercase, char separator)
+{
+ int count = strlen(addr);
+
+ if (count != 17) {
+ psmove_WARNING("Invalid address: '%s'\n", addr);
+ return NULL;
+ }
+
+ char *result = malloc(count + 1);
+ int i;
+
+ for (i=0; i<strlen(addr); i++) {
+ if (addr[i] >= 'A' && addr[i] <= 'F' && i % 3 != 2) {
+ if (lowercase) {
+ result[i] = tolower(addr[i]);
+ } else {
+ result[i] = addr[i];
+ }
+ } else if (addr[i] >= '0' && addr[i] <= '9' && i % 3 != 2) {
+ result[i] = addr[i];
+ } else if (addr[i] >= 'a' && addr[i] <= 'f' && i % 3 != 2) {
+ if (lowercase) {
+ result[i] = addr[i];
+ } else {
+ result[i] = toupper(addr[i]);
+ }
+ } else if ((addr[i] == ':' || addr[i] == '-') && i % 3 == 2) {
+ result[i] = separator;
+ } else {
+ psmove_WARNING("Invalid character at pos %d: '%c'\n", i, addr[i]);
+ free(result);
+ return NULL;
+ }
+ }
+
+ result[count] = '\0';
+ return result;
+}
+
View
4 src/psmove_calibration.c
@@ -272,9 +272,7 @@ psmove_calibration_new(PSMove *move)
psmove_calibration_load(calibration);
if (!psmove_calibration_supported(calibration)) {
if (psmove_connection_type(move) == Conn_USB) {
-#ifdef PSMOVE_DEBUG
- fprintf(stderr, "[PSMOVE] Storing calibration from USB\n");
-#endif
+ psmove_DEBUG("Storing calibration from USB\n");
psmove_calibration_read_from_usb(calibration);
psmove_calibration_save(calibration);
}
View
9 src/psmove_orientation.c
@@ -64,9 +64,7 @@ psmove_orientation_new(PSMove *move)
psmove_return_val_if_fail(move != NULL, NULL);
if (!psmove_has_calibration(move)) {
-#ifdef PSMOVE_DEBUG
- fprintf(stderr, "[PSMOVE] Can't create orientation - no calibration!\n");
-#endif
+ psmove_DEBUG("Can't create orientation - no calibration!\n");
return NULL;
}
@@ -102,10 +100,7 @@ psmove_orientation_update(PSMoveOrientation *orientation)
if (now - orientation->sample_freq_measure_start >= 1000) {
float measured = ((float)orientation->sample_freq_measure_count) /
((float)(now-orientation->sample_freq_measure_start))*1000.;
-
-#ifdef PSMOVE_DEBUG
- printf("[PSMOVE] Measured sample_freq: %f\n", measured);
-#endif
+ psmove_DEBUG("Measured sample_freq: %f\n", measured);
orientation->sample_freq = measured;
orientation->sample_freq_measure_start = now;
View
37 src/psmove_private.h
@@ -53,17 +53,32 @@ extern "C" {
#define PSMOVE_VID 0x054c
#define PSMOVE_PID 0x03d5
-/* Macro: Print a warning message */
-#define psmove_WARNING(x, ...) \
- {fprintf(stderr, "[PSMOVE] " x, __VA_ARGS__);}
+#define psmove_PRINTF(section, msg, ...) \
+ fprintf(stderr, "[" section "] " msg, ## __VA_ARGS__)
+
+/* Macro: Debugging output */
+#ifdef PSMOVE_DEBUG
+# define psmove_DEBUG(msg, ...) \
+ psmove_PRINTF("PSMOVE DEBUG", msg, ## __VA_ARGS__)
+#else
+# define psmove_DEBUG(msg, ...)
+#endif
+
+/* Macro: Warning message */
+#define psmove_WARNING(msg, ...) \
+ psmove_PRINTF("PSMOVE WARNING", msg, ## __VA_ARGS__)
/* Macro: Print a critical message if an assertion fails */
#define psmove_CRITICAL(x) \
- {fprintf(stderr, "[PSMOVE] Assertion fail in %s: %s\n", __func__, x);}
+ psmove_PRINTF("PSMOVE CRITICAL", \
+ "Assertion fail in %s: %s\n", \
+ __func__, x)
/* Macro: Deprecated functions */
#define psmove_DEPRECATED(x) \
- {fprintf(stderr, "[PSMOVE] %s is deprecated: %s\n", __func__, x);}
+ psmove_PRINTF("PSMOVE DEPRECATED", \
+ "%s is deprecated: %s\n", \
+ __func__, x)
/* Macros: Return immediately if an assertion fails + log */
#define psmove_return_if_fail(expr) \
@@ -164,6 +179,18 @@ ADDAPI char *
ADDCALL _psmove_btaddr_to_string(const PSMove_Data_BTAddr addr);
/**
+ * Normalize a Bluetooth address given a specific format
+ *
+ * lowercase ... Make all characters lowercase if nonzero
+ * separator ... The separator character (usually ':' or '-')
+ *
+ * The return value must be free()d by the caller.
+ **/
+ADDAPI char *
+ADDCALL _psmove_normalize_btaddr(const char *addr, int lowercase, char separator);
+
+
+/**
* Read the current Bluetooth addresses stored in the controller
*
* This only works via USB.
View
17 src/tracker/camera_control.c
@@ -33,6 +33,8 @@
#include "../external/iniparser/dictionary.h"
#include "../external/iniparser/iniparser.h"
+#include "../psmove_private.h"
+
#include <stdio.h>
#include "camera_control_private.h"
@@ -83,10 +85,7 @@ camera_control_new(int cameraID)
char *video = psmove_util_get_env_string(PSMOVE_TRACKER_FILENAME_ENV);
if (video) {
-#ifdef PSMOVE_DEBUG
- fprintf(stderr, "[PSMOVE] Using '%s' as video input.\n",
- video);
-#endif
+ psmove_DEBUG("Using '%s' as video input.\n", video);
cc->capture = cvCaptureFromFile(video);
free(video);
} else {
@@ -156,17 +155,9 @@ camera_control_query_frame(CameraControl* cc)
result = cc->frame3ch;
#else
-
-#ifdef PSMOVE_DEBUG
long start = psmove_util_get_ticks();
-#endif
-
result = cvQueryFrame(cc->capture);
-
-#ifdef PSMOVE_DEBUG
- printf("cvQueryFrame: %ld ms\n", psmove_util_get_ticks() - start);
-#endif
-
+ psmove_DEBUG("cvQueryFrame: %ld ms\n", psmove_util_get_ticks() - start);
#endif
#if defined(PSMOVE_USE_DEINTERLACE)
View
4 src/tracker/platform/psmove_linuxsupport.c
@@ -66,9 +66,7 @@ linux_find_pseye()
&capability) == 0, -1);
if (strcmp((const char*)(capability.driver), "ov534") == 0) {
-#ifdef PSMOVE_DEBUG
- fprintf(stderr, "[PSMOVE] Detected PSEye (ov534): %s\n", filename);
-#endif
+ psmove_DEBUG("Detected PSEye (ov534): %s\n", filename);
result = i;
break;
}
View
14 src/tracker/psmove_tracker.c
@@ -402,11 +402,9 @@ PSMoveTracker *psmove_tracker_new() {
int camera_env = psmove_util_get_env_int(PSMOVE_TRACKER_CAMERA_ENV);
if (camera_env != -1) {
-#ifdef PSMOVE_DEBUG
camera = camera_env;
- fprintf(stderr, "[PSMOVE] Using camera %d (%s is set)\n",
- camera, PSMOVE_TRACKER_CAMERA_ENV);
-#endif
+ psmove_DEBUG("Using camera %d (%s is set)\n", camera,
+ PSMOVE_TRACKER_CAMERA_ENV);
}
return psmove_tracker_new_with_camera(camera);
@@ -435,9 +433,7 @@ psmove_tracker_new_with_camera(int camera) {
int dimming_env = psmove_util_get_env_int(PSMOVE_TRACKER_DIMMING_ENV);
if (dimming_env != -1) {
dimming_env = MIN(100, MAX(1, dimming_env));
-#ifdef PSMOVE_DEBUG
- fprintf(stderr, "[PSMOVE] Dimming factor: %d %%\n", dimming_env);
-#endif
+ psmove_DEBUG("Dimming factor: %d %%\n", dimming_env);
tracker->dimming_factor = (float)dimming_env * .01;
} else {
tracker->dimming_factor = 1.;
@@ -510,9 +506,7 @@ psmove_tracker_new_with_camera(int camera) {
if (size == -1) {
size = MIN(frame->width/2, frame->height/2);
} else {
-#ifdef PSMOVE_DEBUG
- fprintf(stderr, "[PSMOVE] Using ROI size: %d\n", size);
-#endif
+ psmove_DEBUG("Using ROI size: %d\n", size);
}
int w = size, h = size;
View
6 src/utils/psmovepair.c
@@ -79,7 +79,7 @@ int main(int argc, char* argv[])
}
if (psmove_connection_type(move) != Conn_Bluetooth) {
- printf("PSMove #%d connected via USB. ", i+1);
+ printf("PSMove #%d connected via USB.\n", i+1);
int result = 0;
if (custom_addr) {
@@ -89,12 +89,12 @@ int main(int argc, char* argv[])
}
if (result) {
- printf("Pairing succeeded!\n");
+ printf("Pairing of #%d succeeded!\n", i+1);
char *serial = psmove_get_serial(move);
printf("Controller address: %s\n", serial);
free(serial);
} else {
- printf("Pairing failed.\n");
+ printf("Pairing of #%d failed.\n", i+1);
}
if (psmove_has_calibration(move)) {

0 comments on commit d039777

Please sign in to comment.