From 47f5351a1735e8a452839b169d65093a11f84f73 Mon Sep 17 00:00:00 2001 From: Alexander Nitsch Date: Sun, 8 Jul 2018 16:26:29 +0200 Subject: [PATCH 01/14] Handle multiple PIDs in psmove_connect_by_id --- src/psmove.c | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/src/psmove.c b/src/psmove.c index f4ed941b..c22ef78f 100644 --- a/src/psmove.c +++ b/src/psmove.c @@ -134,6 +134,9 @@ psmove_decode_16bit(char *data, int offset) return (low | (high << 8)) - 0x8000; } +#define NUM_PSMOVE_PIDS \ + ((sizeof(PSMOVE_PIDS) / sizeof(PSMOVE_PIDS[0])) - 1) + static int PSMOVE_PIDS[] = { PSMOVE_PID, @@ -738,6 +741,8 @@ compare_hid_device_info_ptr(const void *a, const void *b) return 0; } +static struct hid_device_info *move_hid_devices[NUM_PSMOVE_PIDS]; + PSMove * psmove_connect_by_id(int id) { @@ -767,22 +772,38 @@ psmove_connect_by_id(int id) struct hid_device_info *devs, *cur_dev; PSMove *move = NULL; - // TODO: FIXME: This needs to add support for PS4 Move still - devs = hid_enumerate(PSMOVE_VID, PSMOVE_PID); + // TODO: Implement handling of multiple PIDs in a cleaner way. Ideally, we + // would just build a *single* list of hid_device_info structs. + + // enumerate matching HID devices + for (unsigned int i = 0; i < NUM_PSMOVE_PIDS; i++) { + psmove_DEBUG("Enumerating HID devices with PID 0x%04X\n", PSMOVE_PIDS[i]); + + // NOTE: hidapi returns NULL for PIDs that were not found + move_hid_devices[i] = hid_enumerate(PSMOVE_VID, PSMOVE_PIDS[i]); + } // Count available devices int available = 0; - for (cur_dev=devs, available=0; cur_dev != NULL; cur_dev = cur_dev->next, available++); + int i; + for (i = 0; i < NUM_PSMOVE_PIDS; i++) { + for (cur_dev = move_hid_devices[i]; cur_dev != NULL; cur_dev = cur_dev->next, available++); + } + psmove_DEBUG("Matching HID devices: %d\n", available); // Sort list of devices to have stable ordering of devices + int n = 0; struct hid_device_info **devs_sorted = calloc(available, sizeof(struct hid_device_info *)); - cur_dev = devs; - int i; - for (i=0; inext; + for (i = 0; i < NUM_PSMOVE_PIDS; i++) { + cur_dev = move_hid_devices[i]; + while (cur_dev && (n < available)) { + devs_sorted[n] = cur_dev; + cur_dev = cur_dev->next; + n++; + } } qsort((void *)devs_sorted, available, sizeof(struct hid_device_info *), compare_hid_device_info_ptr); + #if defined(PSMOVE_DEBUG) for (i=0; i Date: Sun, 8 Jul 2018 16:38:59 +0200 Subject: [PATCH 02/14] Switch from HID Output report 0x02 to 0x06 Both seem to do the same thing (setting LED color and rumble), but 0x06 also works with the CECH-ZCM2 models. --- src/psmove.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/psmove.c b/src/psmove.c index c22ef78f..1dd4aaea 100644 --- a/src/psmove.c +++ b/src/psmove.c @@ -51,7 +51,7 @@ /* Begin private definitions */ /* Buffer size for writing LEDs and reading sensor data */ -#define PSMOVE_BUFFER_SIZE 49 +#define PSMOVE_BUFFER_SIZE 9 /* Buffer size for the Bluetooth address get request */ #define PSMOVE_BTADDR_GET_SIZE 16 @@ -71,7 +71,7 @@ enum PSMove_Request_Type { PSMove_Req_GetInput = 0x01, - PSMove_Req_SetLEDs = 0x02, + PSMove_Req_SetLEDs = 0x06, PSMove_Req_SetLEDPWMFrequency = 0x03, PSMove_Req_GetBTAddr = 0x04, PSMove_Req_SetBTAddr = 0x05, From 154bdc0f5e66eed6621ca2ede73da66a3c084f0f Mon Sep 17 00:00:00 2001 From: Alexander Nitsch Date: Sun, 8 Jul 2018 18:28:25 +0200 Subject: [PATCH 03/14] Prepare for handling different controller models --- include/psmove.h | 10 ++++++++- src/daemon/moved.cpp | 15 ++++++++++--- src/platform/psmove_port_linux.cpp | 36 +++++++++++++++++------------- src/platform/psmove_port_osx.mm | 4 +++- src/platform/psmove_port_windows.c | 2 ++ src/psmove.c | 22 ++++++++++++------ src/psmove_port.h | 2 +- src/psmove_private.h | 2 +- src/psmoveapi.cpp | 6 ++++- src/utils/psmoveregister.c | 16 ++++++++++--- 10 files changed, 81 insertions(+), 34 deletions(-) diff --git a/include/psmove.h b/include/psmove.h index 03071181..6292b9f2 100644 --- a/include/psmove.h +++ b/include/psmove.h @@ -49,6 +49,13 @@ extern "C" { # define ADDCALL #endif +/*! Hardware model type for controller. + **/ +enum PSMove_Model_Type { + Model_ZCM1, + Model_ZCM2, +}; + /*! Connection type for controllers. * Controllers can be connected via USB or via Bluetooth. The USB connection is * required when you want to pair the controller and for saving the calibration @@ -416,9 +423,10 @@ ADDCALL psmove_pair(PSMove *move); * \brief Add an entry for a controller paired on another host. * * \param addr The Bluetooth address of the PS move to add + * \param model The hardware model type of the controller **/ ADDAPI enum PSMove_Bool -ADDCALL psmove_host_pair_custom(const char *addr); +ADDCALL psmove_host_pair_custom(const char *addr, enum PSMove_Model_Type model); /** * \brief Pair a controller connected via USB to a specific address. diff --git a/src/daemon/moved.cpp b/src/daemon/moved.cpp index d9d4ee8c..4691a89f 100644 --- a/src/daemon/moved.cpp +++ b/src/daemon/moved.cpp @@ -100,7 +100,11 @@ on_monitor_update_moved(enum MonitorEvent event, if (event == EVENT_DEVICE_ADDED) { if (device_type == EVENT_DEVICE_TYPE_USB) { - PSMove *move = psmove_connect_internal(serial, path, -1); + // TODO: FIXME: This should use the device's actual USB product ID. + // HACK: We rely on this invalid PID being translated to a + // valid controller model (the old ZCM1, be default). + unsigned short pid = 0; + PSMove *move = psmove_connect_internal(serial, path, -1, pid); if (psmove_pair(move)) { // Indicate to the user that pairing was successful psmove_set_leds(move, 0, 255, 0); @@ -270,7 +274,8 @@ moved_server::handle_request() char *addr = _psmove_btaddr_to_string(*((PSMove_Data_BTAddr *)&request.register_controller.btaddr)); char *host = psmove_port_get_host_bluetooth_address(); - psmove_port_register_psmove(addr, host); + // TODO: Add support for other models + psmove_port_register_psmove(addr, host, Model_ZCM1); free(addr); free(host); @@ -304,7 +309,11 @@ psmove_dev::psmove_dev(move_daemon *moved, const char *path, const wchar_t *seri : dirty_output(0) { if (path != NULL) { - move = psmove_connect_internal((wchar_t *)serial, (char *)path, moved->count()); + // TODO: FIXME: This should use the device's actual USB product ID. + // HACK: We rely on this invalid PID being translated to a + // valid controller model (the old ZCM1, be default). + unsigned short pid = 0; + move = psmove_connect_internal((wchar_t *)serial, (char *)path, moved->count(), pid); } else { move = psmove_connect_by_id(moved->count()); } diff --git a/src/platform/psmove_port_linux.cpp b/src/platform/psmove_port_linux.cpp index b7924a1a..fbe96ccb 100644 --- a/src/platform/psmove_port_linux.cpp +++ b/src/platform/psmove_port_linux.cpp @@ -57,20 +57,22 @@ namespace { const std::string -BLUEZ5_INFO_ENTRY { -"[General]\n" -"Name=Motion Controller\n" -"Class=0x002508\n" -"SupportedTechnologies=BR/EDR\n" -"Trusted=true\n" -"Blocked=false\n" -"Services=00001124-0000-1000-8000-00805f9b34fb;\n" -"\n" -"[DeviceID]\n" -"Source=1\n" -"Vendor=1356\n" -"Product=981\n" -"Version=1\n" }; +BLUEZ5_INFO_ENTRY(unsigned short pid) { + return + "[General]\n" + "Name=Motion Controller\n" + "Class=0x002508\n" + "SupportedTechnologies=BR/EDR\n" + "Trusted=true\n" + "Blocked=false\n" + "Services=00001124-0000-1000-8000-00805f9b34fb;\n" + "\n" + "[DeviceID]\n" + "Source=1\n" + "Vendor=1356\n" + "Product=" + std::to_string(pid) + "\n" + "Version=1\n"; +}; const std::string BLUEZ5_CACHE_ENTRY { @@ -360,11 +362,13 @@ psmove_port_get_host_bluetooth_address() } void -psmove_port_register_psmove(const char *addr, const char *host) +psmove_port_register_psmove(const char *addr, const char *host, PSMove_Model_Type model) { auto controller_addr = cstring_to_stdstring_free(_psmove_normalize_btaddr(addr, 0, ':')); auto host_addr = cstring_to_stdstring_free(_psmove_normalize_btaddr(host, 0, ':')); + unsigned short pid = (model == Model_ZCM2) ? PSMOVE_PS4_PID : PSMOVE_PID; + if (controller_addr.empty()) { LINUXPAIR_DEBUG("Cannot parse controller address: '%s'\n", addr); return; @@ -403,7 +407,7 @@ psmove_port_register_psmove(const char *addr, const char *host) } } - if (!linux_bluez5_update_file_content(bluetoothd, info_dir + "/info", BLUEZ5_INFO_ENTRY)) { + if (!linux_bluez5_update_file_content(bluetoothd, info_dir + "/info", BLUEZ5_INFO_ENTRY(pid))) { return; } diff --git a/src/platform/psmove_port_osx.mm b/src/platform/psmove_port_osx.mm index e8dedc16..2e2c206c 100644 --- a/src/platform/psmove_port_osx.mm +++ b/src/platform/psmove_port_osx.mm @@ -241,8 +241,10 @@ }; void -psmove_port_register_psmove(const char *addr, const char *host) +psmove_port_register_psmove(const char *addr, const char *host, PSMove_Model_Type model) { + // TODO: FIXME: If necessary, handle different controller models differently. + ScopedNSAutoreleasePool pool; std::string btaddr = cstring_to_stdstring_free(_psmove_normalize_btaddr(addr, 1, '-')); diff --git a/src/platform/psmove_port_windows.c b/src/platform/psmove_port_windows.c index f6a955a3..c3c6f93f 100644 --- a/src/platform/psmove_port_windows.c +++ b/src/platform/psmove_port_windows.c @@ -669,6 +669,8 @@ psmove_port_get_host_bluetooth_address() void psmove_port_register_psmove(const char *addr, const char *host) { + // TODO: FIXME: If necessary, handle different controller models differently. + // FIXME: Host is ignored for now HANDLE hRadio; diff --git a/src/psmove.c b/src/psmove.c index 1dd4aaea..0e9c0a15 100644 --- a/src/psmove.c +++ b/src/psmove.c @@ -212,6 +212,9 @@ struct _PSMove { PSMove_Data_LEDs leds; PSMove_Data_Input input; + /* Controller hardware model */ + enum PSMove_Model_Type model; + /* Save location for the serial number */ char *serial_number; @@ -427,7 +430,7 @@ psmove_count_connected() } PSMove * -psmove_connect_internal(const wchar_t *serial, const char *path, int id) +psmove_connect_internal(const wchar_t *serial, const char *path, int id, unsigned short pid) { char *tmp; @@ -499,6 +502,8 @@ psmove_connect_internal(const wchar_t *serial, const char *path, int id) /* Message type for LED set requests */ move->leds.type = PSMove_Req_SetLEDs; + move->model = (pid == PSMOVE_PS4_PID) ? Model_ZCM2 : Model_ZCM1; + /* Remember the ID/index */ move->id = id; @@ -694,6 +699,9 @@ psmove_connect_remote_by_id(int id, moved_client *client, int remote_id) /* Message type for LED set requests */ move->leds.type = PSMove_Req_SetLEDs; + // TODO: Add support for other models + move->model = Model_ZCM1; + /* Remember the ID/index */ move->id = id; @@ -820,7 +828,7 @@ psmove_connect_by_id(int id) if (strstr(cur_dev->path, "&col01#") != NULL) { if (count == id) { - move = psmove_connect_internal(cur_dev->serial_number, cur_dev->path, id); + move = psmove_connect_internal(cur_dev->serial_number, cur_dev->path, id, cur_dev->product_id); break; } } else { @@ -832,7 +840,7 @@ psmove_connect_by_id(int id) #else if (id < available) { cur_dev = devs_sorted[id]; - move = psmove_connect_internal(cur_dev->serial_number, cur_dev->path, id); + move = psmove_connect_internal(cur_dev->serial_number, cur_dev->path, id, cur_dev->product_id); } #endif @@ -1034,7 +1042,7 @@ psmove_pair(PSMove *move) char *addr = psmove_get_serial(move); - psmove_port_register_psmove(addr, host); + psmove_port_register_psmove(addr, host, move->model); free(addr); free(host); @@ -1043,13 +1051,13 @@ psmove_pair(PSMove *move) } enum PSMove_Bool -psmove_host_pair_custom(const char *addr) +psmove_host_pair_custom(const char *addr, enum PSMove_Model_Type model) { char *host = psmove_port_get_host_bluetooth_address(); psmove_return_val_if_fail(host != NULL, PSMove_False); - psmove_port_register_psmove(addr, host); + psmove_port_register_psmove(addr, host, model); free(host); @@ -1083,7 +1091,7 @@ psmove_pair_custom(PSMove *move, const char *new_host_string) char *addr = psmove_get_serial(move); char *host = _psmove_btaddr_to_string(new_host); - psmove_port_register_psmove(addr, host); + psmove_port_register_psmove(addr, host, move->model); free(addr); free(host); diff --git a/src/psmove_port.h b/src/psmove_port.h index ef996717..098b1e50 100644 --- a/src/psmove_port.h +++ b/src/psmove_port.h @@ -112,7 +112,7 @@ ADDCALL psmove_port_get_host_bluetooth_address(); * Add the PS Move Bluetooth controller address to the system database **/ ADDAPI void -ADDCALL psmove_port_register_psmove(const char *addr, const char *host); +ADDCALL psmove_port_register_psmove(const char *addr, const char *host, enum PSMove_Model_Type model); #ifdef __cplusplus } diff --git a/src/psmove_private.h b/src/psmove_private.h index e0596785..13c0c654 100644 --- a/src/psmove_private.h +++ b/src/psmove_private.h @@ -130,7 +130,7 @@ ADDCALL _psmove_read_data(PSMove *move, unsigned char *data, int length); * [PRIVATE API] Internal device open function (hidraw, Linux / for moved) **/ ADDAPI PSMove * -ADDCALL psmove_connect_internal(const wchar_t *serial, const char *path, int id); +ADDCALL psmove_connect_internal(const wchar_t *serial, const char *path, int id, unsigned short pid); /** * [PRIVATE API] Get device path of a controller (hidraw, Linux / for moved) diff --git a/src/psmoveapi.cpp b/src/psmoveapi.cpp index d3df06db..e69a97a6 100644 --- a/src/psmoveapi.cpp +++ b/src/psmoveapi.cpp @@ -299,7 +299,11 @@ PSMoveAPI::on_monitor_event(enum MonitorEvent event, enum MonitorEventDeviceType } } - PSMove *move = psmove_connect_internal(serial, path, -1); + // TODO: FIXME: This should use the device's actual USB product ID. + // HACK: We rely on this invalid PID being translated to a + // valid controller model (the old ZCM1, be default). + unsigned short pid = 0; + PSMove *move = psmove_connect_internal(serial, path, -1, pid); if (move == nullptr) { psmove_CRITICAL("Cannot open move for retrieving serial!"); return; diff --git a/src/utils/psmoveregister.c b/src/utils/psmoveregister.c index c90bc8b2..f7cbc447 100644 --- a/src/utils/psmoveregister.c +++ b/src/utils/psmoveregister.c @@ -38,12 +38,14 @@ #include "../psmove_private.h" #include "../psmove_port.h" +#define OPT_PS4 "--ps4" + int main(int argc, char *argv[]) { - if (argc != 2) { - fprintf(stderr, "Usage: %s [bluetooth-address]\n", argv[0]); + if (argc < 2) { + fprintf(stderr, "Usage: %s [%s] bluetooth-address\n", argv[0], OPT_PS4); return 1; } @@ -51,7 +53,15 @@ main(int argc, char *argv[]) return 1; } - if (psmove_host_pair_custom(argv[1])) { + char *bdaddr = argv[1]; + PSMove_Model_Type model = Model_ZCM1; + + if ((argc > 2) && (strcmp(argv[1], OPT_PS4) == 0)) { + bdaddr = argv[2]; + model = Model_ZCM2; + } + + if (psmove_host_pair_custom(bdaddr, model)) { printf("Paired\n"); } else { printf("Pairing failed\n"); From 459183f66e6c821f084ad42b4da96efe80014625 Mon Sep 17 00:00:00 2001 From: Alexander Nitsch Date: Sun, 8 Jul 2018 19:46:22 +0200 Subject: [PATCH 04/14] Add BlueZ agent for registering PS4 Move controllers This agent simply answers any HCI PIN Code Request with the PIN code '0000'. --- contrib/.gitignore | 2 + contrib/bluezutils.py | 49 +++++++++++ contrib/pin-agent.py | 194 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 contrib/.gitignore create mode 100644 contrib/bluezutils.py create mode 100644 contrib/pin-agent.py diff --git a/contrib/.gitignore b/contrib/.gitignore new file mode 100644 index 00000000..2f78cf5b --- /dev/null +++ b/contrib/.gitignore @@ -0,0 +1,2 @@ +*.pyc + diff --git a/contrib/bluezutils.py b/contrib/bluezutils.py new file mode 100644 index 00000000..7d0b3132 --- /dev/null +++ b/contrib/bluezutils.py @@ -0,0 +1,49 @@ +# From https://git.kernel.org/pub/scm/bluetooth/bluez.git/plain/test/bluezutils.py + +import dbus + +SERVICE_NAME = "org.bluez" +ADAPTER_INTERFACE = SERVICE_NAME + ".Adapter1" +DEVICE_INTERFACE = SERVICE_NAME + ".Device1" + +def get_managed_objects(): + bus = dbus.SystemBus() + manager = dbus.Interface(bus.get_object("org.bluez", "/"), + "org.freedesktop.DBus.ObjectManager") + return manager.GetManagedObjects() + +def find_adapter(pattern=None): + return find_adapter_in_objects(get_managed_objects(), pattern) + +def find_adapter_in_objects(objects, pattern=None): + bus = dbus.SystemBus() + for path, ifaces in objects.items(): + adapter = ifaces.get(ADAPTER_INTERFACE) + if adapter is None: + continue + if not pattern or pattern == adapter["Address"] or \ + path.endswith(pattern): + obj = bus.get_object(SERVICE_NAME, path) + return dbus.Interface(obj, ADAPTER_INTERFACE) + raise Exception("Bluetooth adapter not found") + +def find_device(device_address, adapter_pattern=None): + return find_device_in_objects(get_managed_objects(), device_address, + adapter_pattern) + +def find_device_in_objects(objects, device_address, adapter_pattern=None): + bus = dbus.SystemBus() + path_prefix = "" + if adapter_pattern: + adapter = find_adapter_in_objects(objects, adapter_pattern) + path_prefix = adapter.object_path + for path, ifaces in objects.items(): + device = ifaces.get(DEVICE_INTERFACE) + if device is None: + continue + if (device["Address"] == device_address and + path.startswith(path_prefix)): + obj = bus.get_object(SERVICE_NAME, path) + return dbus.Interface(obj, DEVICE_INTERFACE) + + raise Exception("Bluetooth device not found") diff --git a/contrib/pin-agent.py b/contrib/pin-agent.py new file mode 100644 index 00000000..526dd2da --- /dev/null +++ b/contrib/pin-agent.py @@ -0,0 +1,194 @@ +#!/usr/bin/python + +# Registers a BlueZ D-Bus agent that automatically answers any PIN Code +# Request with PIN code '0000'. +# +# Adapted from https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/test/simple-agent +# + + +from __future__ import absolute_import, print_function, unicode_literals + +from optparse import OptionParser +import sys +import dbus +import dbus.service +import dbus.mainloop.glib +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject +import bluezutils + +BUS_NAME = 'org.bluez' +AGENT_INTERFACE = 'org.bluez.Agent1' +AGENT_PATH = "/test/agent" + +PIN_CODE = "0000" + +bus = None +device_obj = None +dev_path = None + +def ask(prompt): + try: + return raw_input(prompt) + except: + return input(prompt) + +def set_trusted(path): + props = dbus.Interface(bus.get_object("org.bluez", path), + "org.freedesktop.DBus.Properties") + props.Set("org.bluez.Device1", "Trusted", True) + +def dev_connect(path): + dev = dbus.Interface(bus.get_object("org.bluez", path), + "org.bluez.Device1") + dev.Connect() + +class Rejected(dbus.DBusException): + _dbus_error_name = "org.bluez.Error.Rejected" + +class Agent(dbus.service.Object): + exit_on_release = True + + def set_exit_on_release(self, exit_on_release): + self.exit_on_release = exit_on_release + + @dbus.service.method(AGENT_INTERFACE, + in_signature="", out_signature="") + def Release(self): + print("Release") + if self.exit_on_release: + mainloop.quit() + + @dbus.service.method(AGENT_INTERFACE, + in_signature="os", out_signature="") + def AuthorizeService(self, device, uuid): + print("AuthorizeService (%s, %s)" % (device, uuid)) + authorize = ask("Authorize connection (yes/no): ") + if (authorize == "yes"): + return + raise Rejected("Connection rejected by user") + + @dbus.service.method(AGENT_INTERFACE, + in_signature="o", out_signature="s") + def RequestPinCode(self, device): + print("RequestPinCode (%s)" % (device)) + set_trusted(device) + print("Sending PIN code '%s'" % (PIN_CODE)) + return PIN_CODE + + @dbus.service.method(AGENT_INTERFACE, + in_signature="o", out_signature="u") + def RequestPasskey(self, device): + print("RequestPasskey (%s)" % (device)) + set_trusted(device) + passkey = ask("Enter passkey: ") + return dbus.UInt32(passkey) + + @dbus.service.method(AGENT_INTERFACE, + in_signature="ouq", out_signature="") + def DisplayPasskey(self, device, passkey, entered): + print("DisplayPasskey (%s, %06u entered %u)" % + (device, passkey, entered)) + + @dbus.service.method(AGENT_INTERFACE, + in_signature="os", out_signature="") + def DisplayPinCode(self, device, pincode): + print("DisplayPinCode (%s, %s)" % (device, pincode)) + + @dbus.service.method(AGENT_INTERFACE, + in_signature="ou", out_signature="") + def RequestConfirmation(self, device, passkey): + print("RequestConfirmation (%s, %06d)" % (device, passkey)) + confirm = ask("Confirm passkey (yes/no): ") + if (confirm == "yes"): + set_trusted(device) + return + raise Rejected("Passkey doesn't match") + + @dbus.service.method(AGENT_INTERFACE, + in_signature="o", out_signature="") + def RequestAuthorization(self, device): + print("RequestAuthorization (%s)" % (device)) + auth = ask("Authorize? (yes/no): ") + if (auth == "yes"): + return + raise Rejected("Pairing rejected") + + @dbus.service.method(AGENT_INTERFACE, + in_signature="", out_signature="") + def Cancel(self): + print("Cancel") + +def pair_reply(): + print("Device paired") + set_trusted(dev_path) + dev_connect(dev_path) + mainloop.quit() + +def pair_error(error): + err_name = error.get_dbus_name() + if err_name == "org.freedesktop.DBus.Error.NoReply" and device_obj: + print("Timed out. Cancelling pairing") + device_obj.CancelPairing() + else: + print("Creating device failed: %s" % (error)) + + + mainloop.quit() + +if __name__ == '__main__': + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + bus = dbus.SystemBus() + + capability = "KeyboardDisplay" + + parser = OptionParser() + parser.add_option("-i", "--adapter", action="store", + type="string", + dest="adapter_pattern", + default=None) + parser.add_option("-c", "--capability", action="store", + type="string", dest="capability") + parser.add_option("-t", "--timeout", action="store", + type="int", dest="timeout", + default=60000) + (options, args) = parser.parse_args() + if options.capability: + capability = options.capability + + path = "/test/agent" + agent = Agent(bus, path) + + mainloop = GObject.MainLoop() + + obj = bus.get_object(BUS_NAME, "/org/bluez"); + manager = dbus.Interface(obj, "org.bluez.AgentManager1") + manager.RegisterAgent(path, capability) + + print("Agent registered. Now press the controller's PS button.") + + # Fix-up old style invocation (BlueZ 4) + if len(args) > 0 and args[0].startswith("hci"): + options.adapter_pattern = args[0] + del args[:1] + + if len(args) > 0: + device = bluezutils.find_device(args[0], + options.adapter_pattern) + dev_path = device.object_path + agent.set_exit_on_release(False) + device.Pair(reply_handler=pair_reply, error_handler=pair_error, + timeout=60000) + device_obj = device + else: + manager.RequestDefaultAgent(path) + + mainloop.run() + + #adapter.UnregisterAgent(path) + #print("Agent unregistered") + From 9a6fe1a352b779df0beedf5737a633b1180de507 Mon Sep 17 00:00:00 2001 From: Alexander Nitsch Date: Sun, 8 Jul 2018 20:11:52 +0200 Subject: [PATCH 05/14] Properly decode accel/gyro values for PS4 Move controllers Those values are stored in the HID report as two's complement. Also, there are no longer values from two different half-frames. --- src/psmove.c | 79 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 25 deletions(-) diff --git a/src/psmove.c b/src/psmove.c index 0e9c0a15..3e17a014 100644 --- a/src/psmove.c +++ b/src/psmove.c @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -1603,19 +1604,33 @@ psmove_get_accelerometer(PSMove *move, int *ax, int *ay, int *az) { psmove_return_if_fail(move != NULL); - if (ax != NULL) { - *ax = ((move->input.aXlow + move->input.aXlow2) + - ((move->input.aXhigh + move->input.aXhigh2) << 8)) / 2 - 0x8000; - } + if (move->model == Model_ZCM2) { + if (ax != NULL) { + *ax = (int16_t) (move->input.aXlow + (move->input.aXhigh << 8)); + } - if (ay != NULL) { - *ay = ((move->input.aYlow + move->input.aYlow2) + - ((move->input.aYhigh + move->input.aYhigh2) << 8)) / 2 - 0x8000; - } + if (ay != NULL) { + *ay = (int16_t) (move->input.aYlow + (move->input.aYhigh << 8)); + } - if (az != NULL) { - *az = ((move->input.aZlow + move->input.aZlow2) + - ((move->input.aZhigh + move->input.aZhigh2) << 8)) / 2 - 0x8000; + if (az != NULL) { + *az = (int16_t) (move->input.aZlow + (move->input.aZhigh << 8)); + } + } else { + if (ax != NULL) { + *ax = ((move->input.aXlow + move->input.aXlow2) + + ((move->input.aXhigh + move->input.aXhigh2) << 8)) / 2 - 0x8000; + } + + if (ay != NULL) { + *ay = ((move->input.aYlow + move->input.aYlow2) + + ((move->input.aYhigh + move->input.aYhigh2) << 8)) / 2 - 0x8000; + } + + if (az != NULL) { + *az = ((move->input.aZlow + move->input.aZlow2) + + ((move->input.aZhigh + move->input.aZhigh2) << 8)) / 2 - 0x8000; + } } } @@ -1624,20 +1639,34 @@ psmove_get_gyroscope(PSMove *move, int *gx, int *gy, int *gz) { psmove_return_if_fail(move != NULL); - if (gx != NULL) { - *gx = ((move->input.gXlow + move->input.gXlow2) + - ((move->input.gXhigh + move->input.gXhigh2) << 8)) / 2 - 0x8000; - } - - if (gy != NULL) { - *gy = ((move->input.gYlow + move->input.gYlow2) + - ((move->input.gYhigh + move->input.gYhigh2) << 8)) / 2 - 0x8000; - } - - if (gz != NULL) { - *gz = ((move->input.gZlow + move->input.gZlow2) + - ((move->input.gZhigh + move->input.gZhigh2) << 8)) / 2 - 0x8000; - } + if (move->model == Model_ZCM2) { + if (gx != NULL) { + *gx = (int16_t) (move->input.gXlow + (move->input.gXhigh << 8)); + } + + if (gy != NULL) { + *gy = (int16_t) (move->input.gYlow + (move->input.gYhigh << 8)); + } + + if (gz != NULL) { + *gz = (int16_t) (move->input.gZlow + (move->input.gZhigh << 8)); + } + } else { + if (gx != NULL) { + *gx = ((move->input.gXlow + move->input.gXlow2) + + ((move->input.gXhigh + move->input.gXhigh2) << 8)) / 2 - 0x8000; + } + + if (gy != NULL) { + *gy = ((move->input.gYlow + move->input.gYlow2) + + ((move->input.gYhigh + move->input.gYhigh2) << 8)) / 2 - 0x8000; + } + + if (gz != NULL) { + *gz = ((move->input.gZlow + move->input.gZlow2) + + ((move->input.gZhigh + move->input.gZhigh2) << 8)) / 2 - 0x8000; + } + } } void From 22453dfb168e374c59a96773016b84d72c9b18b2 Mon Sep 17 00:00:00 2001 From: Alexander Nitsch Date: Sun, 8 Jul 2018 20:19:49 +0200 Subject: [PATCH 06/14] Return zeros for PS4 Move controller's magnetometer readings This model does not have any magnetometers. --- src/psmove.c | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/psmove.c b/src/psmove.c index 3e17a014..cf2237e0 100644 --- a/src/psmove.c +++ b/src/psmove.c @@ -1780,19 +1780,35 @@ psmove_get_magnetometer(PSMove *move, int *mx, int *my, int *mz) { psmove_return_if_fail(move != NULL); - if (mx != NULL) { - *mx = TWELVE_BIT_SIGNED(((move->input.templow_mXhigh & 0x0F) << 8) | - move->input.mXlow); - } + if (move->model == Model_ZCM2) { + // NOTE: This model does not have magnetometers - if (my != NULL) { - *my = TWELVE_BIT_SIGNED((move->input.mYhigh << 4) | - (move->input.mYlow_mZhigh & 0xF0) >> 4); - } + if (mx != NULL) { + *mx = 0; + } - if (mz != NULL) { - *mz = TWELVE_BIT_SIGNED(((move->input.mYlow_mZhigh & 0x0F) << 8) | - move->input.mZlow); + if (my != NULL) { + *my = 0; + } + + if (mz != NULL) { + *mz = 0; + } + } else { + if (mx != NULL) { + *mx = TWELVE_BIT_SIGNED(((move->input.templow_mXhigh & 0x0F) << 8) | + move->input.mXlow); + } + + if (my != NULL) { + *my = TWELVE_BIT_SIGNED((move->input.mYhigh << 4) | + (move->input.mYlow_mZhigh & 0xF0) >> 4); + } + + if (mz != NULL) { + *mz = TWELVE_BIT_SIGNED(((move->input.mYlow_mZhigh & 0x0F) << 8) | + move->input.mZlow); + } } } From c2db065607b957ffa9115d7e28a1f1c37a5564e5 Mon Sep 17 00:00:00 2001 From: Alexander Nitsch Date: Sun, 8 Jul 2018 20:27:43 +0200 Subject: [PATCH 07/14] Fix trigger value for PS4 Move controllers There are no half-frames in the HID input report anymore. Instead, the second value reported for the trigger button seems to be a heavily low-pass filtered version of the first one. We simply return the first value for this new controller model. --- src/psmove.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/psmove.c b/src/psmove.c index cf2237e0..d7cfee76 100644 --- a/src/psmove.c +++ b/src/psmove.c @@ -1558,7 +1558,11 @@ psmove_get_trigger(PSMove *move) { psmove_return_val_if_fail(move != NULL, 0); - return (move->input.trigger + move->input.trigger2) / 2; + if (move->model == Model_ZCM2) { + return move->input.trigger; + } else { + return (move->input.trigger + move->input.trigger2) / 2; + } } void From 561f516673b5321a4f59f67bb5764a5d2555cdde Mon Sep 17 00:00:00 2001 From: Alexander Nitsch Date: Mon, 9 Jul 2018 09:46:42 +0200 Subject: [PATCH 08/14] Fix changed function signature on Windows --- src/platform/psmove_port_windows.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/psmove_port_windows.c b/src/platform/psmove_port_windows.c index c3c6f93f..4005c71b 100644 --- a/src/platform/psmove_port_windows.c +++ b/src/platform/psmove_port_windows.c @@ -667,7 +667,7 @@ psmove_port_get_host_bluetooth_address() } void -psmove_port_register_psmove(const char *addr, const char *host) +psmove_port_register_psmove(const char *addr, const char *host, PSMove_Model_Type model) { // TODO: FIXME: If necessary, handle different controller models differently. From 6198310202e90e376efc113982f8fd886319a4af Mon Sep 17 00:00:00 2001 From: Alexander Nitsch Date: Mon, 9 Jul 2018 10:01:32 +0200 Subject: [PATCH 09/14] Fix broken type in function declaration Weird, GCC on Linux did not even issue a warning ... --- src/platform/psmove_port_linux.cpp | 2 +- src/platform/psmove_port_osx.mm | 2 +- src/platform/psmove_port_windows.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platform/psmove_port_linux.cpp b/src/platform/psmove_port_linux.cpp index fbe96ccb..ecfd55d0 100644 --- a/src/platform/psmove_port_linux.cpp +++ b/src/platform/psmove_port_linux.cpp @@ -362,7 +362,7 @@ psmove_port_get_host_bluetooth_address() } void -psmove_port_register_psmove(const char *addr, const char *host, PSMove_Model_Type model) +psmove_port_register_psmove(const char *addr, const char *host, enum PSMove_Model_Type model) { auto controller_addr = cstring_to_stdstring_free(_psmove_normalize_btaddr(addr, 0, ':')); auto host_addr = cstring_to_stdstring_free(_psmove_normalize_btaddr(host, 0, ':')); diff --git a/src/platform/psmove_port_osx.mm b/src/platform/psmove_port_osx.mm index 2e2c206c..db3cfc7c 100644 --- a/src/platform/psmove_port_osx.mm +++ b/src/platform/psmove_port_osx.mm @@ -241,7 +241,7 @@ }; void -psmove_port_register_psmove(const char *addr, const char *host, PSMove_Model_Type model) +psmove_port_register_psmove(const char *addr, const char *host, enum PSMove_Model_Type model) { // TODO: FIXME: If necessary, handle different controller models differently. diff --git a/src/platform/psmove_port_windows.c b/src/platform/psmove_port_windows.c index 4005c71b..591d7e1f 100644 --- a/src/platform/psmove_port_windows.c +++ b/src/platform/psmove_port_windows.c @@ -667,7 +667,7 @@ psmove_port_get_host_bluetooth_address() } void -psmove_port_register_psmove(const char *addr, const char *host, PSMove_Model_Type model) +psmove_port_register_psmove(const char *addr, const char *host, enum PSMove_Model_Type model) { // TODO: FIXME: If necessary, handle different controller models differently. From 11e1875ba53f95ee5587531b0da4a765fd3b84a8 Mon Sep 17 00:00:00 2001 From: Alexander Nitsch Date: Sun, 22 Jul 2018 23:32:43 +0200 Subject: [PATCH 10/14] Fix indentation --- src/psmove.c | 54 ++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/psmove.c b/src/psmove.c index d7cfee76..525c77fb 100644 --- a/src/psmove.c +++ b/src/psmove.c @@ -1644,33 +1644,33 @@ psmove_get_gyroscope(PSMove *move, int *gx, int *gy, int *gz) psmove_return_if_fail(move != NULL); if (move->model == Model_ZCM2) { - if (gx != NULL) { - *gx = (int16_t) (move->input.gXlow + (move->input.gXhigh << 8)); - } - - if (gy != NULL) { - *gy = (int16_t) (move->input.gYlow + (move->input.gYhigh << 8)); - } - - if (gz != NULL) { - *gz = (int16_t) (move->input.gZlow + (move->input.gZhigh << 8)); - } - } else { - if (gx != NULL) { - *gx = ((move->input.gXlow + move->input.gXlow2) + - ((move->input.gXhigh + move->input.gXhigh2) << 8)) / 2 - 0x8000; - } - - if (gy != NULL) { - *gy = ((move->input.gYlow + move->input.gYlow2) + - ((move->input.gYhigh + move->input.gYhigh2) << 8)) / 2 - 0x8000; - } - - if (gz != NULL) { - *gz = ((move->input.gZlow + move->input.gZlow2) + - ((move->input.gZhigh + move->input.gZhigh2) << 8)) / 2 - 0x8000; - } - } + if (gx != NULL) { + *gx = (int16_t) (move->input.gXlow + (move->input.gXhigh << 8)); + } + + if (gy != NULL) { + *gy = (int16_t) (move->input.gYlow + (move->input.gYhigh << 8)); + } + + if (gz != NULL) { + *gz = (int16_t) (move->input.gZlow + (move->input.gZhigh << 8)); + } + } else { + if (gx != NULL) { + *gx = ((move->input.gXlow + move->input.gXlow2) + + ((move->input.gXhigh + move->input.gXhigh2) << 8)) / 2 - 0x8000; + } + + if (gy != NULL) { + *gy = ((move->input.gYlow + move->input.gYlow2) + + ((move->input.gYhigh + move->input.gYhigh2) << 8)) / 2 - 0x8000; + } + + if (gz != NULL) { + *gz = ((move->input.gZlow + move->input.gZlow2) + + ((move->input.gZhigh + move->input.gZhigh2) << 8)) / 2 - 0x8000; + } + } } void From abd457e406d20a7650c08552654a38e28b6b89c2 Mon Sep 17 00:00:00 2001 From: Alexander Nitsch Date: Sun, 22 Jul 2018 23:36:02 +0200 Subject: [PATCH 11/14] Replace macro with string variable --- src/utils/psmoveregister.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/psmoveregister.c b/src/utils/psmoveregister.c index f7cbc447..a34de335 100644 --- a/src/utils/psmoveregister.c +++ b/src/utils/psmoveregister.c @@ -38,7 +38,7 @@ #include "../psmove_private.h" #include "../psmove_port.h" -#define OPT_PS4 "--ps4" +static const char *OPT_PS4 = "--ps4"; int From 3a98873a81957c1f1e36e6b7f3c61df79a4e8dd7 Mon Sep 17 00:00:00 2001 From: Alexander Nitsch Date: Sun, 22 Jul 2018 23:41:03 +0200 Subject: [PATCH 12/14] Fix freeing HID enumeration --- src/psmove.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/psmove.c b/src/psmove.c index 525c77fb..672d6dfc 100644 --- a/src/psmove.c +++ b/src/psmove.c @@ -849,7 +849,7 @@ psmove_connect_by_id(int id) // free HID device enumerations for (i = 0; i < NUM_PSMOVE_PIDS; i++) { - devs = hid_enumerate(PSMOVE_VID, PSMOVE_PIDS[i]); + devs = move_hid_devices[i]; if (devs) { hid_free_enumeration(devs); } From 94cb319b35760d908d5f012bb6925cb27531adcd Mon Sep 17 00:00:00 2001 From: Alexander Nitsch Date: Sun, 22 Jul 2018 23:44:19 +0200 Subject: [PATCH 13/14] Fix typo in comments --- src/daemon/moved.cpp | 4 ++-- src/psmoveapi.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/daemon/moved.cpp b/src/daemon/moved.cpp index 4691a89f..1f8c02e4 100644 --- a/src/daemon/moved.cpp +++ b/src/daemon/moved.cpp @@ -102,7 +102,7 @@ on_monitor_update_moved(enum MonitorEvent event, if (device_type == EVENT_DEVICE_TYPE_USB) { // TODO: FIXME: This should use the device's actual USB product ID. // HACK: We rely on this invalid PID being translated to a - // valid controller model (the old ZCM1, be default). + // valid controller model (the old ZCM1, by default). unsigned short pid = 0; PSMove *move = psmove_connect_internal(serial, path, -1, pid); if (psmove_pair(move)) { @@ -311,7 +311,7 @@ psmove_dev::psmove_dev(move_daemon *moved, const char *path, const wchar_t *seri if (path != NULL) { // TODO: FIXME: This should use the device's actual USB product ID. // HACK: We rely on this invalid PID being translated to a - // valid controller model (the old ZCM1, be default). + // valid controller model (the old ZCM1, by default). unsigned short pid = 0; move = psmove_connect_internal((wchar_t *)serial, (char *)path, moved->count(), pid); } else { diff --git a/src/psmoveapi.cpp b/src/psmoveapi.cpp index e69a97a6..50a08c0c 100644 --- a/src/psmoveapi.cpp +++ b/src/psmoveapi.cpp @@ -301,7 +301,7 @@ PSMoveAPI::on_monitor_event(enum MonitorEvent event, enum MonitorEventDeviceType // TODO: FIXME: This should use the device's actual USB product ID. // HACK: We rely on this invalid PID being translated to a - // valid controller model (the old ZCM1, be default). + // valid controller model (the old ZCM1, by default). unsigned short pid = 0; PSMove *move = psmove_connect_internal(serial, path, -1, pid); if (move == nullptr) { From 5431ba7096a8cadf2647b11fe646478f1acea998 Mon Sep 17 00:00:00 2001 From: Alexander Nitsch Date: Sun, 22 Jul 2018 23:59:30 +0200 Subject: [PATCH 14/14] Revert public API changes to psmove_host_pair_custom() Instead, introduce a new function psmove_host_pair_custom_model() that accepts the controller hardware model as additional argument. This keeps the public API stable. --- include/psmove.h | 14 +++++++++++++- src/psmove.c | 10 +++++++++- src/utils/psmoveregister.c | 2 +- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/include/psmove.h b/include/psmove.h index 6292b9f2..88aaba49 100644 --- a/include/psmove.h +++ b/include/psmove.h @@ -423,10 +423,22 @@ ADDCALL psmove_pair(PSMove *move); * \brief Add an entry for a controller paired on another host. * * \param addr The Bluetooth address of the PS move to add + **/ +ADDAPI enum PSMove_Bool +ADDCALL psmove_host_pair_custom(const char *addr); + +/** + * \brief Add an entry for a controller paired on another host. + * + * This function behaves the same as psmove_host_pair_custom() but allows you + * to specify a controller hardware model. Use this to pair a PS4 Move Motion + * controller (model ZCM2). + * + * \param addr The Bluetooth address of the PS move to add * \param model The hardware model type of the controller **/ ADDAPI enum PSMove_Bool -ADDCALL psmove_host_pair_custom(const char *addr, enum PSMove_Model_Type model); +ADDCALL psmove_host_pair_custom_model(const char *addr, enum PSMove_Model_Type model); /** * \brief Pair a controller connected via USB to a specific address. diff --git a/src/psmove.c b/src/psmove.c index 672d6dfc..5c62927a 100644 --- a/src/psmove.c +++ b/src/psmove.c @@ -1052,7 +1052,15 @@ psmove_pair(PSMove *move) } enum PSMove_Bool -psmove_host_pair_custom(const char *addr, enum PSMove_Model_Type model) +psmove_host_pair_custom(const char *addr) +{ + // NOTE: We assume Move Motion controller model ZCM1 to be compatible with + // earlier version of the library that only supported that model. + return psmove_host_pair_custom_model(addr, Model_ZCM1); +} + +enum PSMove_Bool +psmove_host_pair_custom_model(const char *addr, enum PSMove_Model_Type model) { char *host = psmove_port_get_host_bluetooth_address(); diff --git a/src/utils/psmoveregister.c b/src/utils/psmoveregister.c index a34de335..82934374 100644 --- a/src/utils/psmoveregister.c +++ b/src/utils/psmoveregister.c @@ -61,7 +61,7 @@ main(int argc, char *argv[]) model = Model_ZCM2; } - if (psmove_host_pair_custom(bdaddr, model)) { + if (psmove_host_pair_custom_model(bdaddr, model)) { printf("Paired\n"); } else { printf("Pairing failed\n");