diff --git a/autobuild.xml b/autobuild.xml
index e07e92421b915417f3030b1611a5d982c23315fd..9785884a405129e2fb61b6d989a0e40e0f02dfaf 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -1384,9 +1384,9 @@
             <key>archive</key>
             <map>
               <key>hash</key>
-              <string>69c3e7c8cbf47d6f840011d1fa34cd8b</string>
+              <string>a487fff84208a45844602c4a1f68c974</string>
               <key>url</key>
-              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/108791/947410/libndofdev-0.1.577357-darwin64-577357.tar.bz2</string>
+              <string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/76356/727333/libndofdev-0.1.555523-darwin64-555523.tar.bz2</string>
             </map>
             <key>name</key>
             <string>darwin64</string>
@@ -1417,7 +1417,7 @@
           </map>
         </map>
         <key>version</key>
-        <string>0.1.577357</string>
+        <string>0.1.555523</string>
       </map>
       <key>libpng</key>
       <map>
diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h
index 8ee06a2cc14a7e7cebfcea12b3083d5deffab0b8..45049e15395df6a03449c16cedf8539db96d9a8d 100644
--- a/indra/llwindow/llwindow.h
+++ b/indra/llwindow/llwindow.h
@@ -197,7 +197,7 @@ class LLWindow : public LLInstanceTracker<LLWindow>
     // windows only DirectInput8 for joysticks
     virtual void* getDirectInput8() { return NULL; };
     virtual bool getInputDevices(U32 device_type_filter,
-                                 std::function<bool(std::string&, LLSD::Binary&, void*)> osx_callback,
+                                 std::function<bool(std::string&, LLSD&, void*)> osx_callback,
                                  void* win_callback,
                                  void* userdata)
     {
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index 3cb5939dc954508483c672e5264ad0be8427c140..aa87a6803f26430d439e351e6f7ca2eac4e4af49 100644
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -1813,53 +1813,158 @@ void LLWindowMacOSX::spawnWebBrowser(const std::string& escaped_url, bool async)
 	}
 }
 
+// String should match ndof, so string mapping code was copied as is
+static char mapChar( char c )
+{
+    unsigned char uc = ( unsigned char ) c;
+    
+    switch( uc )
+    {
+        case '/': return '-'; // use dash instead of slash
+            
+        case 0x7F: return ' ';
+        case 0x80: return 'A';
+        case 0x81: return 'A';
+        case 0x82: return 'C';
+        case 0x83: return 'E';
+        case 0x84: return 'N';
+        case 0x85: return 'O';
+        case 0x86: return 'U';
+        case 0x87: return 'a';
+        case 0x88: return 'a';
+        case 0x89: return 'a';
+        case 0x8A: return 'a';
+        case 0x8B: return 'a';
+        case 0x8C: return 'a';
+        case 0x8D: return 'c';
+        case 0x8E: return 'e';
+        case 0x8F: return 'e';
+        case 0x90: return ' ';
+        case 0x91: return ' '; // ? '
+        case 0x92: return ' '; // ? '
+        case 0x93: return ' '; // ? "
+        case 0x94: return ' '; // ? "
+        case 0x95: return ' ';
+        case 0x96: return ' ';
+        case 0x97: return ' ';
+        case 0x98: return ' ';
+        case 0x99: return ' ';
+        case 0x9A: return ' ';
+        case 0x9B: return 0x27;
+        case 0x9C: return 0x22;
+        case 0x9D: return ' ';
+        case 0x9E: return ' ';
+        case 0x9F: return ' ';
+        case 0xA0: return ' ';
+        case 0xA1: return ' ';
+        case 0xA2: return ' ';
+        case 0xA3: return ' ';
+        case 0xA4: return ' ';
+        case 0xA5: return ' ';
+        case 0xA6: return ' ';
+        case 0xA7: return ' ';
+        case 0xA8: return ' ';
+        case 0xA9: return ' ';
+        case 0xAA: return ' ';
+        case 0xAB: return ' ';
+        case 0xAC: return ' ';
+        case 0xAD: return ' ';
+        case 0xAE: return ' ';
+        case 0xAF: return ' ';
+        case 0xB0: return ' ';
+        case 0xB1: return ' ';
+        case 0xB2: return ' ';
+        case 0xB3: return ' ';
+        case 0xB4: return ' ';
+        case 0xB5: return ' ';
+        case 0xB6: return ' ';
+        case 0xB7: return ' ';
+        case 0xB8: return ' ';
+        case 0xB9: return ' ';
+        case 0xBA: return ' ';
+        case 0xBB: return ' ';
+        case 0xBC: return ' ';
+        case 0xBD: return ' ';
+        case 0xBE: return ' ';
+        case 0xBF: return ' ';
+        case 0xC0: return ' ';
+        case 0xC1: return ' ';
+        case 0xC2: return ' ';
+        case 0xC3: return ' ';
+        case 0xC4: return ' ';
+        case 0xC5: return ' ';
+        case 0xC6: return ' ';
+        case 0xC7: return ' ';
+        case 0xC8: return ' ';
+        case 0xC9: return ' ';
+        case 0xCA: return ' ';
+        case 0xCB: return 'A';
+        case 0xCC: return 'A';
+        case 0xCD: return 'O';
+        case 0xCE: return ' ';
+        case 0xCF: return ' ';
+        case 0xD0: return '-';
+        case 0xD1: return '-';
+        case 0xD2: return 0x22;
+        case 0xD3: return 0x22;
+        case 0xD4: return 0x27;
+        case 0xD5: return 0x27;
+        case 0xD6: return '-'; // use dash instead of slash
+        case 0xD7: return ' ';
+        case 0xD8: return 'y';
+        case 0xD9: return 'Y';
+        case 0xDA: return '-'; // use dash instead of slash
+        case 0xDB: return ' ';
+        case 0xDC: return '<';
+        case 0xDD: return '>';
+        case 0xDE: return ' ';
+        case 0xDF: return ' ';
+        case 0xE0: return ' ';
+        case 0xE1: return ' ';
+        case 0xE2: return ',';
+        case 0xE3: return ',';
+        case 0xE4: return ' ';
+        case 0xE5: return 'A';
+        case 0xE6: return 'E';
+        case 0xE7: return 'A';
+        case 0xE8: return 'E';
+        case 0xE9: return 'E';
+        case 0xEA: return 'I';
+        case 0xEB: return 'I';
+        case 0xEC: return 'I';
+        case 0xED: return 'I';
+        case 0xEE: return 'O';
+        case 0xEF: return 'O';
+        case 0xF0: return ' ';
+        case 0xF1: return 'O';
+        case 0xF2: return 'U';
+        case 0xF3: return 'U';
+        case 0xF4: return 'U';
+        case 0xF5: return '|';
+        case 0xF6: return ' ';
+        case 0xF7: return ' ';
+        case 0xF8: return ' ';
+        case 0xF9: return ' ';
+        case 0xFA: return '.';
+        case 0xFB: return ' ';
+        case 0xFC: return ' ';
+        case 0xFD: return 0x22;
+        case 0xFE: return ' ';
+        case 0xFF: return ' ';
+    }
+    return c;
+}
 
-// Device and Element Interfaces
-
-typedef enum HIDElementTypeMask
-{
-    kHIDElementTypeInput                = 1 << 1,
-    kHIDElementTypeOutput          = 1 << 2,
-    kHIDElementTypeFeature          = 1 << 3,
-    kHIDElementTypeCollection        = 1 << 4,
-    kHIDElementTypeIO                    = kHIDElementTypeInput | kHIDElementTypeOutput | kHIDElementTypeFeature,
-    kHIDElementTypeAll                    = kHIDElementTypeIO | kHIDElementTypeCollection
-}HIDElementTypeMask;
-
-struct hu_element_t
-{
-    unsigned long type;                        // the type defined by IOHIDElementType in IOHIDKeys.h
-    long usage;                                // usage within above page from IOUSBHIDParser.h which defines specific usage
-    long usagePage;                            // usage page from IOUSBHIDParser.h which defines general usage
-    void* cookie;                             // unique value( within device of specific vendorID and productID ) which identifies element, will NOT change
-    long min;                                // reported min value possible
-    long max;                                // reported max value possible
-    long scaledMin;                            // reported scaled min value possible
-    long scaledMax;                            // reported scaled max value possible
-    long size;                                // size in bits of data return from element
-    unsigned char relative;                    // are reports relative to last report( deltas )
-    unsigned char wrapping;                    // does element wrap around( one value higher than max is min )
-    unsigned char nonLinear;                // are the values reported non-linear relative to element movement
-    unsigned char preferredState;            // does element have a preferred state( such as a button )
-    unsigned char nullState;                // does element have null state
-    long units;                                // units value is reported in( not used very often )
-    long unitExp;                            // exponent for units( also not used very often )
-    char name[256];                            // name of element( c string )
-
-    // runtime variables
-    long initialCenter;                     // center value at start up
-    unsigned char hasCenter;                 // whether or not to use center for calibration
-    long minReport;                         // min returned value
-    long maxReport;                         // max returned value( calibrate call )
-    long userMin;                             // user set value to scale to( scale call )
-    long userMax;
-
-    struct hu_element_t* pPrevious;            // previous element( NULL at list head )
-    struct hu_element_t* pChild;            // next child( only of collections )
-    struct hu_element_t* pSibling;            // next sibling( for elements and collections )
-
-    long depth;
-};
+// String should match ndof for manufacturer based search to work
+static void sanitizeString( char* inCStr )
+{
+    char* charIt = inCStr;
+    while ( *charIt )
+    {
+        *charIt = mapChar( *charIt );
+        charIt++;
+    }
+}
 
 struct HidDevice
 {
@@ -1871,20 +1976,6 @@ struct HidDevice
     long mUsagePage;
 };
 
-/*************************************************************************
-*
-* hu_BuildDevice( inHIDDevice )
-*
-* Purpose:  given a IO device object build a flat device record including device info and all elements
-*
-* Notes:    handles NULL lists properly
-*
-* Inputs:   inHIDDevice        - the I/O device object
-*
-* Returns:  hu_device_t*    - the address of the new device record
-*/
-
-
 static void populate_device_info( io_object_t io_obj_p, CFDictionaryRef device_dic, HidDevice* devicep )
 {
     CFMutableDictionaryRef io_properties = nil;
@@ -1927,6 +2018,7 @@ static void populate_device_info( io_object_t io_obj_p, CFDictionaryRef device_d
             if ( dict_element )
             {
                 bool res = CFStringGetCString((CFStringRef)dict_element, devicep->mProduct, 256, kCFStringEncodingUTF8);
+                sanitizeString(devicep->mProduct);
                 if ( !res )
                 {
                     LL_WARNS("Joystick") << "Failed to populate mProduct" << LL_ENDL;
@@ -1941,6 +2033,7 @@ static void populate_device_info( io_object_t io_obj_p, CFDictionaryRef device_d
             if ( dict_element )
             {
                 bool res = CFStringGetCString( (CFStringRef)dict_element, devicep->mManufacturer, 256, kCFStringEncodingUTF8 );
+                sanitizeString(devicep->mManufacturer);
                 if ( !res )
                 {
                     LL_WARNS("Joystick") << "Failed to populate mManufacturer" << LL_ENDL;
@@ -2115,18 +2208,31 @@ static void get_devices(std::list<HidDevice> &list_of_devices,
     {
         HidDevice device = populate_device( io_obj );
         
-        if (device.mAxis >= 3
-            || (device.mUsagePage == kHIDPage_GenericDesktop
-                && (device.mUsage == kHIDUsage_GD_MultiAxisController
-                    || device.mUsage == kHIDUsage_GD_GamePad
-                    || device.mUsage == kHIDUsage_GD_Joystick))
-            || (device.mUsagePage == kHIDPage_Game
-                && (device.mUsage == kHIDUsage_Game_3DGameController))
-            || strstr(device.mManufacturer, "3Dconnexion"))
+        if (debugLoggingEnabled("Joystick"))
         {
             list_of_devices.push_back(device);
+            LL_DEBUGS("Joystick") << "Device axises: " << (S32)device.mAxis
+                                  << "Device HIDUsepage: " << (S32)device.mUsagePage
+                                  << "Device HIDUsage: " << (S32)device.mUsage
+                                  << LL_ENDL;
+        }
+        else
+        {
+            //  Should match ndof
+            if (device.mAxis >= 3
+                || (device.mUsagePage == kHIDPage_GenericDesktop
+                    && (device.mUsage == kHIDUsage_GD_MultiAxisController
+                        || device.mUsage == kHIDUsage_GD_GamePad
+                        || device.mUsage == kHIDUsage_GD_Joystick))
+                || (device.mUsagePage == kHIDPage_Game
+                    && device.mUsage == kHIDUsage_Game_3DGameController)
+                || strstr(device.mManufacturer, "3Dconnexion"))
+            {
+                list_of_devices.push_back(device);
+            }
         }
         
+        
         // release the device object, it is no longer needed
         result = IOObjectRelease( io_obj );
         if ( KERN_SUCCESS != result )
@@ -2137,7 +2243,7 @@ static void get_devices(std::list<HidDevice> &list_of_devices,
 }
 
 bool LLWindowMacOSX::getInputDevices(U32 device_type_filter,
-                                     std::function<bool(std::string&, LLSD::Binary&, void*)> osx_callback,
+                                     std::function<bool(std::string&, LLSD&, void*)> osx_callback,
                                      void* win_callback,
                                      void* userdata)
 {
@@ -2172,11 +2278,10 @@ bool LLWindowMacOSX::getInputDevices(U32 device_type_filter,
         
         for (iter = device_list.begin(); iter != device_list.end(); ++iter)
         {
-            S32 size = sizeof(long);
-            LLSD::Binary data; //just an std::vector
-            data.resize(size);
-            memcpy(&data[0], &iter->mLocalID, size);
             std::string label(iter->mProduct);
+            LLSD data;
+            data["manufacturer"] = std::string(iter->mManufacturer);
+            data["product"] = label;
             
             if (osx_callback(label, data, userdata))
             {
diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h
index 679bb13335f1833d590d28b93ecaaa98894b8f01..19136aa3de6612245d5d8a6858f6d4e6d203b630 100644
--- a/indra/llwindow/llwindowmacosx.h
+++ b/indra/llwindow/llwindowmacosx.h
@@ -114,7 +114,7 @@ class LLWindowMacOSX : public LLWindow
 	F32 getSystemUISize() override;
     
     bool getInputDevices(U32 device_type_filter,
-                         std::function<bool(std::string&, LLSD::Binary&, void*)> osx_callback,
+                         std::function<bool(std::string&, LLSD&, void*)> osx_callback,
                          void* win_callback,
                          void* userdata) override;
 
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index 3c7922648bfe54aed360f1b45127d7f0c3b91594..60bd3b080d972ccb387037a1d3d7c8989eda531e 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -4496,7 +4496,7 @@ void* LLWindowWin32::getDirectInput8()
 }
 
 bool LLWindowWin32::getInputDevices(U32 device_type_filter
-                                    std::function<bool(std::string&, LLSD::Binary&, void*)> osx_callback,
+                                    std::function<bool(std::string&, LLSD&, void*)> osx_callback,
                                     void * di8_devices_callback,
                                     void* userdata)
 {
diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h
index dcefe6dab2945df51e17ecbf5fca938165ad5e99..b0d5c557b8a00a3db9983f19a3af55aa7ea963f1 100644
--- a/indra/llwindow/llwindowwin32.h
+++ b/indra/llwindow/llwindowwin32.h
@@ -130,7 +130,7 @@ class LLWindowWin32 : public LLWindow
 
     /*virtual*/ void* getDirectInput8();
     /*virtual*/ bool getInputDevices(U32 device_type_filter,
-                                     std::function<bool(std::string&, LLSD::Binary&, void*)> osx_callback,
+                                     std::function<bool(std::string&, LLSD&, void*)> osx_callback,
                                      void* win_callback,
                                      void* userdata);
 
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index ff7232830b24c6f9c358163388fa350c4638c5aa..3e17c434a3ab005e4f9172ecfa3c66d850b20ca9 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -3770,7 +3770,7 @@
     <key>Persist</key>
     <integer>1</integer>
     <key>Type</key>
-    <string>String</string>
+    <string>LLSD</string>
     <key>Value</key>
     <string />
   </map>
diff --git a/indra/newview/llfloaterjoystick.cpp b/indra/newview/llfloaterjoystick.cpp
index 18e5858e8df91825ad341660be2ab8777767b223..d3add020cf523e8d7daf92f40b51e9ced96cc458 100644
--- a/indra/newview/llfloaterjoystick.cpp
+++ b/indra/newview/llfloaterjoystick.cpp
@@ -250,7 +250,7 @@ void LLFloaterJoystick::refresh()
 	initFromSettings();
 }
 
-bool LLFloaterJoystick::addDeviceCallback(std::string &name, LLSD::Binary& value, void* userdata)
+bool LLFloaterJoystick::addDeviceCallback(std::string &name, LLSD& value, void* userdata)
 {
     LLFloaterJoystick * floater = (LLFloaterJoystick*)userdata;
     floater->mJoysticksCombo->add(name, value, ADD_BOTTOM, 1);
@@ -280,9 +280,9 @@ void LLFloaterJoystick::refreshListOfDevices()
 #elif LL_DARWIN
     U32 device_type = 0;
 #else
-    // MAC doesn't support device search yet
-    // On MAC there is an ndof_idsearch and it is possible to specify product
-    // and manufacturer in NDOF_Device for ndof_init_first to pick specific one
+    // On MAC it is possible to specify product
+    // and manufacturer in NDOF_Device for
+    // ndof_init_first to pick specific device
     U32 device_type = 0;
 #endif
     if (gViewerWindow->getWindow()->getInputDevices(device_type, addDeviceCallback, win_calback, this))
@@ -427,10 +427,11 @@ void LLFloaterJoystick::onCommitJoystickEnabled(LLUICtrl*, void *joy_panel)
 			joystick->toggleFlycam();
 		}
 	}
-
-    std::string device_id = LLViewerJoystick::getInstance()->getDeviceUUIDString();
-    gSavedSettings.setString("JoystickDeviceUUID", device_id);
-    LL_DEBUGS("Joystick") << "Selected " << device_id << " as joystick." << LL_ENDL;
+    
+    LLViewerJoystick::getInstance()->saveDeviceIdToSettings();
+    
+    std::string device_string = LLViewerJoystick::getInstance()->getDeviceUUIDString();
+    LL_DEBUGS("Joystick") << "Selected " << device_string << " as joystick." << LL_ENDL;
 
     self->refreshListOfDevices();
 }
diff --git a/indra/newview/llfloaterjoystick.h b/indra/newview/llfloaterjoystick.h
index e7049ab906ac804e42f0cf25f6fcf93b4057fdda..ff889c804beed3c4b63bbcb8dbe5d0f87dca409d 100644
--- a/indra/newview/llfloaterjoystick.h
+++ b/indra/newview/llfloaterjoystick.h
@@ -46,7 +46,7 @@ class LLFloaterJoystick : public LLFloater
 	virtual void draw();
 	static  void setSNDefaults();
 
-    static bool addDeviceCallback(std::string &name, LLSD::Binary& value, void* userdata);
+    static bool addDeviceCallback(std::string &name, LLSD& value, void* userdata);
     void addDevice(std::string &name, LLSD& value);
 
 protected:
diff --git a/indra/newview/llviewerjoystick.cpp b/indra/newview/llviewerjoystick.cpp
index 95cfa5cd786d7cdeeec17e85273f832cc1c57140..ef96ea4493befed459aac17d8d7304ade902f7bb 100644
--- a/indra/newview/llviewerjoystick.cpp
+++ b/indra/newview/llviewerjoystick.cpp
@@ -229,18 +229,11 @@ std::string string_from_guid(const GUID &guid)
 }
 #elif LL_DARWIN
 
-bool macos_devices_callback(std::string &product_name, LLSD::Binary &data, void* userdata)
+bool macos_devices_callback(std::string &product_name, LLSD &data, void* userdata)
 {
-    S32 size = sizeof(long);
-    long id;
-    memcpy(&id, &data[0], size);
+    std::string product = data["product"].asString();
     
-    NDOF_Device *device = ndof_idsearch(id);
-    if (device)
-    {
-        return LLViewerJoystick::getInstance()->initDevice(device, data);
-    }
-    return false;
+    return LLViewerJoystick::getInstance()->initDevice(nullptr, product, data);
 }
 
 #endif
@@ -384,7 +377,7 @@ void LLViewerJoystick::init(bool autoenable)
         {
             U32 device_type = 0;
             void* win_callback = nullptr;
-            std::function<bool(std::string&, LLSD::Binary&, void*)> osx_callback;
+            std::function<bool(std::string&, LLSD&, void*)> osx_callback;
             // di8_devices_callback callback is immediate and happens in scope of getInputDevices()
 #if LL_WINDOWS && !LL_MESA_HEADLESS
             // space navigator is marked as DI8DEVCLASS_GAMECTRL in ndof lib
@@ -393,27 +386,24 @@ void LLViewerJoystick::init(bool autoenable)
 #elif LL_DARWIN
             osx_callback = macos_devices_callback;
             
-            if (mLastDeviceUUID.isBinary())
+            if (mLastDeviceUUID.isMap())
             {
-                S32 size = sizeof(long);
-                long id;
-                memcpy(&id, &mLastDeviceUUID[0], size);
+                std::string manufacturer = mLastDeviceUUID["manufacturer"].asString();
+                std::string product = mLastDeviceUUID["product"].asString();
+                
+                strncpy(mNdofDev->manufacturer, manufacturer.c_str(), sizeof(mNdofDev->manufacturer));
+                strncpy(mNdofDev->product, product.c_str(), sizeof(mNdofDev->product));
                 
-                // todo: search by manufcturer instead, locid might have changed
-                NDOF_Device *device = ndof_idsearch(id);
-                if (device)
+                if (ndof_init_first(mNdofDev, nullptr))
                 {
-                    if (ndof_init_first(device, nullptr))
-                    {
-                        mDriverState = JDS_INITIALIZING;
-                        // Saved device no longer exist
-                        LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL;
-                    }
-                    else
-                    {
-                        mNdofDev = device;
-                        mDriverState = JDS_INITIALIZED;
-                    }
+                    mDriverState = JDS_INITIALIZING;
+                    // Saved device no longer exist
+                    // No device found
+                    LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL;
+                }
+                else
+                {
+                    mDriverState = JDS_INITIALIZED;
                 }
             }
 #endif
@@ -483,7 +473,7 @@ void LLViewerJoystick::initDevice(LLSD &guid)
     mLastDeviceUUID = guid;
     U32 device_type = 0;
     void* win_callback = nullptr;
-    std::function<bool(std::string&, LLSD::Binary&, void*)> osx_callback;
+    std::function<bool(std::string&, LLSD&, void*)> osx_callback;
     mDriverState = JDS_INITIALIZING;
     
 #if LL_WINDOWS && !LL_MESA_HEADLESS
@@ -492,27 +482,24 @@ void LLViewerJoystick::initDevice(LLSD &guid)
     win_callback = &di8_devices_callback;
 #elif LL_DARWIN
     osx_callback = macos_devices_callback;
-    if (mLastDeviceUUID.isBinary())
+    if (mLastDeviceUUID.isMap())
     {
-        S32 size = sizeof(long);
-        long id;
-        memcpy(&id, &mLastDeviceUUID[0], size);
+        std::string manufacturer = mLastDeviceUUID["manufacturer"].asString();
+        std::string product = mLastDeviceUUID["product"].asString();
         
-        NDOF_Device *device = ndof_idsearch(id);
-        if (device)
+        strncpy(mNdofDev->manufacturer, manufacturer.c_str(), sizeof(mNdofDev->manufacturer));
+        strncpy(mNdofDev->product, product.c_str(), sizeof(mNdofDev->product));
+        
+        if (ndof_init_first(mNdofDev, nullptr))
         {
-            // todo: search by manufcturer instead, locid might have changed
-            if (ndof_init_first(device, nullptr))
-            {
-                mDriverState = JDS_INITIALIZING;
-                // Saved device no longer exist
-                LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL;
-            }
-            else
-            {
-                mNdofDev = device;
-                mDriverState = JDS_INITIALIZED;
-            }
+            mDriverState = JDS_INITIALIZING;
+            // Saved device no longer exist
+            // Np other device present
+            LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL;
+        }
+        else
+        {
+            mDriverState = JDS_INITIALIZED;
         }
     }
 #endif
@@ -522,7 +509,7 @@ void LLViewerJoystick::initDevice(LLSD &guid)
         if (!gViewerWindow->getWindow()->getInputDevices(device_type, osx_callback, win_callback, NULL))
         {
             LL_INFOS("Joystick") << "Failed to gather input devices. Falling back to ndof's init" << LL_ENDL;
-            // Failed to gather devices from windows, init first suitable one
+            // Failed to gather devices from window, init first suitable one
             void *preffered_device = NULL;
             mLastDeviceUUID = LLSD();
             initDevice(preffered_device);
@@ -537,19 +524,37 @@ void LLViewerJoystick::initDevice(LLSD &guid)
 #endif
 }
 
-void LLViewerJoystick::initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/, std::string &name, LLSD &guid)
+bool LLViewerJoystick::initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/, std::string &name, LLSD &guid)
 {
 #if LIB_NDOF
     mLastDeviceUUID = guid;
-
+    
+#if LL_DARWIN
+    if (guid.isMap())
+    {
+        std::string manufacturer = mLastDeviceUUID["manufacturer"].asString();
+        std::string product = mLastDeviceUUID["product"].asString();
+        
+        strncpy(mNdofDev->manufacturer, manufacturer.c_str(), sizeof(mNdofDev->manufacturer));
+        strncpy(mNdofDev->product, product.c_str(), sizeof(mNdofDev->product));
+    }
+    else
+    {
+        mNdofDev->product[0] = '\0';
+        mNdofDev->manufacturer[0] = '\0';
+    }
+#else
     strncpy(mNdofDev->product, name.c_str(), sizeof(mNdofDev->product));
     mNdofDev->manufacturer[0] = '\0';
+#endif
 
-    initDevice(preffered_device);
+    return initDevice(preffered_device);
+#else
+    return false;
 #endif
 }
 
-void LLViewerJoystick::initDevice(void * preffered_device /* LPDIRECTINPUTDEVICE8* */)
+bool LLViewerJoystick::initDevice(void * preffered_device /* LPDIRECTINPUTDEVICE8* */)
 {
 #if LIB_NDOF
     // Different joysticks will return different ranges of raw values.
@@ -579,23 +584,6 @@ void LLViewerJoystick::initDevice(void * preffered_device /* LPDIRECTINPUTDEVICE
     else
     {
         mDriverState = JDS_INITIALIZED;
-    }
-#endif
-}
-
-bool LLViewerJoystick::initDevice(NDOF_Device * ndof_device, LLSD::Binary &data)
-{
-    mLastDeviceUUID = data;
-#if LIB_NDOF
-    if (ndof_init_first(ndof_device, nullptr))
-    {
-        mDriverState = JDS_UNINITIALIZED;
-        LL_WARNS() << "ndof_init_first FAILED" << LL_ENDL;
-    }
-    else
-    {
-        mNdofDev = ndof_device;
-        mDriverState = JDS_INITIALIZED;
         return true;
     }
 #endif
@@ -1407,6 +1395,8 @@ bool LLViewerJoystick::isDeviceUUIDSet()
 #if LL_WINDOWS && !LL_MESA_HEADLESS
     // for ease of comparison and to dial less with platform specific variables, we store id as LLSD binary
     return mLastDeviceUUID.isBinary();
+#elif LL_DARWIN
+    return mLastDeviceUUID.isMap();
 #else
     return false;
 #endif
@@ -1434,13 +1424,11 @@ std::string LLViewerJoystick::getDeviceUUIDString()
         return std::string();
     }
 #elif LL_DARWIN
-    if (mLastDeviceUUID.isBinary())
+    if (mLastDeviceUUID.isMap())
     {
-        S32 size = sizeof(long);
-        LLSD::Binary data = mLastDeviceUUID.asBinary();
-        long id;
-        memcpy(&id, &data[0], size);
-        return std::to_string(id);
+        std::string manufacturer = mLastDeviceUUID["manufacturer"].asString();
+        std::string product = mLastDeviceUUID["product"].asString();
+        return manufacturer + ":" + product;
     }
     else
     {
@@ -1451,13 +1439,32 @@ std::string LLViewerJoystick::getDeviceUUIDString()
 #endif
 }
 
+void LLViewerJoystick::saveDeviceIdToSettings()
+{
+#if LL_WINDOWS && !LL_MESA_HEADLESS
+    // can't save as binary directly,
+    // someone editing the xml will corrupt it
+    // so convert to string first
+    std::string device_string = getDeviceUUIDString();
+    gSavedSettings.setLLSD("JoystickDeviceUUID", LLSD(device_string);
+#else
+    LLSD device_id = getDeviceUUID();
+    gSavedSettings.setLLSD("JoystickDeviceUUID", device_id);
+#endif
+}
+
 void LLViewerJoystick::loadDeviceIdFromSettings()
 {
+    LLSD dev_id = gSavedSettings.getLLSD("JoystickDeviceUUID");
 #if LL_WINDOWS && !LL_MESA_HEADLESS
     // We can't save binary data to gSavedSettings, somebody editing the file will corrupt it,
     // so _GUID data gets converted to string (we probably can convert it to LLUUID with memcpy)
     // and here we need to convert it back to binary from string
-    std::string device_string = gSavedSettings.getString("JoystickDeviceUUID");
+    std::string device_string;
+    if (dev_id.isString())
+    {
+        device_string = dev_id.asString();
+    }
     if (device_string.empty())
     {
         mLastDeviceUUID = LLSD();
@@ -1475,21 +1482,17 @@ void LLViewerJoystick::loadDeviceIdFromSettings()
         mLastDeviceUUID = LLSD(data);
     }
 #elif LL_DARWIN
-    std::string device_string = gSavedSettings.getString("JoystickDeviceUUID");
-    if (device_string.empty())
+    if (!dev_id.isMap())
     {
         mLastDeviceUUID = LLSD();
     }
     else
     {
-        LL_DEBUGS("Joystick") << "Looking for device by id: " << device_string << LL_ENDL;
-        long id = std::stol(device_string);
-        S32 size = sizeof(long);
-        LLSD::Binary data; //just an std::vector
-        data.resize(size);
-        memcpy(&data[0], &id, size);
+        std::string manufacturer = mLastDeviceUUID["manufacturer"].asString();
+        std::string product = mLastDeviceUUID["product"].asString();
+        LL_DEBUGS("Joystick") << "Looking for device by manufacturer: " << manufacturer << " and product: " << product <<  LL_ENDL;
         // We store this data in LLSD since it can handle both GUID2 and long
-        mLastDeviceUUID = LLSD(data);
+        mLastDeviceUUID = dev_id;
     }
 #else
     mLastDeviceUUID = LLSD();
diff --git a/indra/newview/llviewerjoystick.h b/indra/newview/llviewerjoystick.h
index 0e5629497d7cdef8b75c2f5f7f3628b6f3fd27a4..33579f544cd56be803774994fdc3ca85c40114ae 100644
--- a/indra/newview/llviewerjoystick.h
+++ b/indra/newview/llviewerjoystick.h
@@ -55,9 +55,8 @@ class LLViewerJoystick : public LLSingleton<LLViewerJoystick>
 public:
 	void init(bool autoenable);
 	void initDevice(LLSD &guid);
-	void initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/);
-	void initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/, std::string &name, LLSD &guid);
-    bool initDevice(NDOF_Device * ndof_device, LLSD::Binary &guid);
+	bool initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/);
+	bool initDevice(void * preffered_device /*LPDIRECTINPUTDEVICE8*/, std::string &name, LLSD &guid);
 	void terminate();
 
 	void updateStatus();
@@ -80,6 +79,7 @@ class LLViewerJoystick : public LLSingleton<LLViewerJoystick>
 	LLSD getDeviceUUID(); //unconverted, OS dependent value wrapped into LLSD, for comparison/search
 	std::string getDeviceUUIDString(); // converted readable value for settings
 	std::string getDescription();
+    void saveDeviceIdToSettings();
 
 protected:
 	void updateEnabled(bool autoenable);