private void HicWorker(object o) { var token = (CancellationToken)o; var nameList = new SortedDictionary<string, string>(); var bStarted = false; var bd = string.Empty; var Buffer = new byte[512]; var BD_Addr = new byte[6]; var BD_Link = new byte[16]; var Transfered = 0; HCI.Event Event; var Command = HCI.Command.HCI_Null; var Connection = new BthConnection(); Log.InfoFormat("-- Bluetooth : HCI_Worker_Thread Starting (IN: {0:X2})", IntIn); HCI_Reset(); while (!token.IsCancellationRequested) { try { if (ReadIntPipe(Buffer, Buffer.Length, ref Transfered) && Transfered > 0) { if (Enum.IsDefined(typeof(HCI.Event), Buffer[0])) { Event = (HCI.Event)Buffer[0]; switch (Event) { case HCI.Event.HCI_Command_Complete_EV: Command = (HCI.Command)(ushort)(Buffer[3] | Buffer[4] << 8); Log.DebugFormat(">> {0} [{1:X2}] [{2:X2}] [{3}]", Event, Buffer[0], Buffer[5], Command); break; case HCI.Event.HCI_Command_Status_EV: Command = (HCI.Command)(ushort)(Buffer[4] | Buffer[5] << 8); Log.DebugFormat(">> {0} [{1:X2}] [{2:X2}] [{3}]", Event, Buffer[0], Buffer[2], Command); if (Buffer[2] != 0) { switch (Command) { case HCI.Command.HCI_Write_Simple_Pairing_Mode: case HCI.Command.HCI_Write_Authentication_Enable: case HCI.Command.HCI_Set_Event_Mask: GlobalConfiguration.Instance.DisableSSP = true; Log.Warn( "-- Simple Pairing not supported on this device. [SSP Disabled]"); Transfered = HCI_Write_Scan_Enable(); break; } } break; case HCI.Event.HCI_Number_Of_Completed_Packets_EV: break; default: Log.DebugFormat(">> {0} [{1:X2}]", Event, Buffer[0]); break; } switch (Event) { case HCI.Event.HCI_Command_Complete_EV: if (Command == HCI.Command.HCI_Reset && Buffer[5] == 0 && !bStarted) { bStarted = true; Thread.Sleep(250); Transfered = HCI_Read_BD_Addr(); } if (Command == HCI.Command.HCI_Read_BD_ADDR && Buffer[5] == 0) { _localMac = new[] { Buffer[6], Buffer[7], Buffer[8], Buffer[9], Buffer[10], Buffer[11] }; Transfered = HCI_Read_Buffer_Size(); } if (Command == HCI.Command.HCI_Read_Buffer_Size && Buffer[5] == 0) { Log.DebugFormat("-- {0:X2}{1:X2}, {2:X2}, {3:X2}{4:X2}, {5:X2}{6:X2}", Buffer[7], Buffer[6], Buffer[8], Buffer[10], Buffer[9], Buffer[12], Buffer[11]); Transfered = HCI_Read_Local_Version_Info(); } if (Command == HCI.Command.HCI_Read_Local_Version_Info && Buffer[5] == 0) { HciVersion = string.Format("{0}.{1:X4}", Buffer[6], Buffer[8] << 8 | Buffer[7]); LmpVersion = string.Format("{0}.{1:X4}", Buffer[9], Buffer[13] << 8 | Buffer[12]); Log.InfoFormat("-- Master {0}, HCI_Version {1}, LMP_Version {2}", Local, HciVersion, LmpVersion); if (GlobalConfiguration.Instance.DisableSSP) { Transfered = HCI_Write_Scan_Enable(); } else { Transfered = HCI_Write_Simple_Pairing_Mode(); } } if (Command == HCI.Command.HCI_Write_Simple_Pairing_Mode) { if (Buffer[5] == 0) { Transfered = HCI_Write_Simple_Pairing_Debug_Mode(); } else { GlobalConfiguration.Instance.DisableSSP = true; Log.Warn("-- Simple Pairing not supported on this device. [SSP Disabled]"); Transfered = HCI_Write_Scan_Enable(); } } if (Command == HCI.Command.HCI_Write_Simple_Pairing_Debug_Mode) { Transfered = HCI_Write_Authentication_Enable(); } if (Command == HCI.Command.HCI_Write_Authentication_Enable) { if (Buffer[5] == 0) { Transfered = HCI_Set_Event_Mask(); } else { GlobalConfiguration.Instance.DisableSSP = true; Log.Warn("-- Simple Pairing not supported on this device. [SSP Disabled]"); Transfered = HCI_Write_Scan_Enable(); } } if (Command == HCI.Command.HCI_Set_Event_Mask) { if (Buffer[5] == 0) { Transfered = HCI_Write_Page_Timeout(); } else { GlobalConfiguration.Instance.DisableSSP = true; Log.Warn("-- Simple Pairing not supported on this device. [SSP Disabled]"); Transfered = HCI_Write_Scan_Enable(); } } if (Command == HCI.Command.HCI_Write_Page_Timeout && Buffer[5] == 0) { Transfered = HCI_Write_Page_Scan_Activity(); } if (Command == HCI.Command.HCI_Write_Page_Scan_Activity && Buffer[5] == 0) { Transfered = HCI_Write_Page_Scan_Type(); } if (Command == HCI.Command.HCI_Write_Page_Scan_Type && Buffer[5] == 0) { Transfered = HCI_Write_Inquiry_Scan_Activity(); } if (Command == HCI.Command.HCI_Write_Inquiry_Scan_Activity && Buffer[5] == 0) { Transfered = HCI_Write_Inquiry_Scan_Type(); } if (Command == HCI.Command.HCI_Write_Inquiry_Scan_Type && Buffer[5] == 0) { Transfered = HCI_Write_Inquiry_Mode(); } if (Command == HCI.Command.HCI_Write_Inquiry_Mode && Buffer[5] == 0) { Transfered = HCI_Write_Class_of_Device(); } if (Command == HCI.Command.HCI_Write_Class_of_Device && Buffer[5] == 0) { Transfered = HCI_Write_Extended_Inquiry_Response(); } if (Command == HCI.Command.HCI_Write_Extended_Inquiry_Response && Buffer[5] == 0) { Transfered = HCI_Write_Local_Name(); } if (Command == HCI.Command.HCI_Write_Local_Name && Buffer[5] == 0) { Transfered = HCI_Write_Scan_Enable(); } if (Command == HCI.Command.HCI_Write_Scan_Enable && Buffer[5] == 0) { Initialised = true; } break; case HCI.Event.HCI_Connection_Request_EV: for (var i = 0; i < 6; i++) BD_Addr[i] = Buffer[i + 2]; Transfered = HCI_Delete_Stored_Link_Key(BD_Addr); Transfered = HCI_Remote_Name_Request(BD_Addr); break; case HCI.Event.HCI_Connection_Complete_EV: bd = string.Format("{0:X2}:{1:X2}:{2:X2}:{3:X2}:{4:X2}:{5:X2}", Buffer[10], Buffer[9], Buffer[8], Buffer[7], Buffer[6], Buffer[5]); if (!nameList.Any()) break; Connection = Add(Buffer[3], (byte)(Buffer[4] | 0x20), nameList[bd]); // TODO: fix workaround, breaks my controller if (Buffer[10] != 0x00 || Buffer[9] != 0x07 || Buffer[8] != 0x04) { //Connection.IsFake = true; //Log.Info("-- Fake DualShock3 found, workaround applied"); Log.Info("-- Fake DualShock3 found"); } else { //Connection.IsFake = false; Log.Info("-- Genuine Sony DualShock3 found"); } // fetch configuration from .INI var bdc = IniConfig.Instance.BthDongle; // check if current device matches names or MACs if (bdc.SupportedNames.Any(n => nameList[bd].Contains(n)) || bdc.SupportedMacs.Any(m => bd.StartsWith(m))) { Connection.ServiceByPass = true; } Connection.RemoteName = nameList[bd]; nameList.Remove(bd); Connection.BdAddress = new[] { Buffer[10], Buffer[9], Buffer[8], Buffer[7], Buffer[6], Buffer[5] }; break; case HCI.Event.HCI_Disconnection_Complete_EV: Remove(Buffer[3], (byte)(Buffer[4] | 0x20)); break; case HCI.Event.HCI_Number_Of_Completed_Packets_EV: for (byte Index = 0, Ptr = 3; Index < Buffer[2]; Index++, Ptr += 4) { OnCompletedCount(Buffer[Ptr], (byte)(Buffer[Ptr + 1] | 0x20), (ushort)(Buffer[Ptr + 2] | Buffer[Ptr + 3] << 8)); } break; case HCI.Event.HCI_Remote_Name_Request_Complete_EV: bd = string.Format("{0:X2}:{1:X2}:{2:X2}:{3:X2}:{4:X2}:{5:X2}", Buffer[8], Buffer[7], Buffer[6], Buffer[5], Buffer[4], Buffer[3]); var nm = new StringBuilder(); for (var Index = 9; Index < Buffer.Length; Index++) { if (Buffer[Index] > 0) nm.Append((char)Buffer[Index]); else break; } var Name = nm.ToString(); Log.InfoFormat("-- Remote Name : {0} - {1}", bd, Name); for (var i = 0; i < 6; i++) BD_Addr[i] = Buffer[i + 3]; var hci = IniConfig.Instance.Hci; if (hci.SupportedNames.Any(n => Name.StartsWith(n)) || hci.SupportedNames.Any(n => Name == n)) { nameList.Add(bd, nm.ToString()); Transfered = HCI_Accept_Connection_Request(BD_Addr, 0x00); } else { Transfered = HCI_Reject_Connection_Request(BD_Addr, 0x0F); } break; case HCI.Event.HCI_Link_Key_Request_EV: for (var i = 0; i < 6; i++) BD_Addr[i] = Buffer[i + 2]; Transfered = HCI_Link_Key_Request_Reply(BD_Addr); Transfered = HCI_Set_Connection_Encryption(Connection.HciHandle); break; case HCI.Event.HCI_PIN_Code_Request_EV: for (var i = 0; i < 6; i++) BD_Addr[i] = Buffer[i + 2]; Transfered = HCI_PIN_Code_Request_Negative_Reply(BD_Addr); break; case HCI.Event.HCI_IO_Capability_Request_EV: Transfered = HCI_IO_Capability_Request_Reply(BD_Addr); break; case HCI.Event.HCI_User_Confirmation_Request_EV: Transfered = HCI_User_Confirmation_Request_Reply(BD_Addr); break; case HCI.Event.HCI_Link_Key_Notification_EV: for (var Index = 0; Index < 6; Index++) BD_Addr[Index] = Buffer[Index + 2]; for (var Index = 0; Index < 16; Index++) BD_Link[Index] = Buffer[Index + 8]; Transfered = HCI_Set_Connection_Encryption(Connection.HciHandle); break; } } } } catch (Exception ex) { Log.ErrorFormat("Unexpected error in HCI_Worker_Thread: {0}", ex); } } HCI_Reset(); Log.Info("-- Bluetooth : HCI_Worker_Thread Exiting"); }
private void HicWorker(object o) { var token = (CancellationToken)o; var nameList = new SortedDictionary <string, string>(); var bStarted = false; var bd = string.Empty; var Buffer = new byte[512]; var BD_Addr = new byte[6]; var BD_Link = new byte[16]; var Transfered = 0; HCI.Event Event; var Command = HCI.Command.HCI_Null; var Connection = new BthConnection(); Log.InfoFormat("-- Bluetooth : HCI_Worker_Thread Starting (IN: {0:X2})", m_IntIn); HCI_Reset(); while (!token.IsCancellationRequested) { try { if (ReadIntPipe(Buffer, Buffer.Length, ref Transfered) && Transfered > 0) { if (Enum.IsDefined(typeof(HCI.Event), Buffer[0])) { Event = (HCI.Event)Buffer[0]; switch (Event) { case HCI.Event.HCI_Command_Complete_EV: Command = (HCI.Command)(ushort)(Buffer[3] | Buffer[4] << 8); Log.DebugFormat(">> {0} [{1:X2}] [{2:X2}] [{3}]", Event, Buffer[0], Buffer[5], Command); break; case HCI.Event.HCI_Command_Status_EV: Command = (HCI.Command)(ushort)(Buffer[4] | Buffer[5] << 8); Log.DebugFormat(">> {0} [{1:X2}] [{2:X2}] [{3}]", Event, Buffer[0], Buffer[2], Command); if (Buffer[2] != 0) { switch (Command) { case HCI.Command.HCI_Write_Simple_Pairing_Mode: case HCI.Command.HCI_Write_Authentication_Enable: case HCI.Command.HCI_Set_Event_Mask: GlobalConfiguration.Instance.DisableSSP = true; Log.Warn( "-- Simple Pairing not supported on this device. [SSP Disabled]"); Transfered = HCI_Write_Scan_Enable(); break; } } break; case HCI.Event.HCI_Number_Of_Completed_Packets_EV: break; default: Log.DebugFormat(">> {0} [{1:X2}]", Event, Buffer[0]); break; } switch (Event) { case HCI.Event.HCI_Command_Complete_EV: if (Command == HCI.Command.HCI_Reset && Buffer[5] == 0 && !bStarted) { bStarted = true; Thread.Sleep(250); Transfered = HCI_Read_BD_Addr(); } if (Command == HCI.Command.HCI_Read_BD_ADDR && Buffer[5] == 0) { _localMac = new[] { Buffer[6], Buffer[7], Buffer[8], Buffer[9], Buffer[10], Buffer[11] }; Transfered = HCI_Read_Buffer_Size(); } if (Command == HCI.Command.HCI_Read_Buffer_Size && Buffer[5] == 0) { Log.DebugFormat("-- {0:X2}{1:X2}, {2:X2}, {3:X2}{4:X2}, {5:X2}{6:X2}", Buffer[7], Buffer[6], Buffer[8], Buffer[10], Buffer[9], Buffer[12], Buffer[11]); Transfered = HCI_Read_Local_Version_Info(); } if (Command == HCI.Command.HCI_Read_Local_Version_Info && Buffer[5] == 0) { HciVersion = string.Format("{0}.{1:X4}", Buffer[6], Buffer[8] << 8 | Buffer[7]); LmpVersion = string.Format("{0}.{1:X4}", Buffer[9], Buffer[13] << 8 | Buffer[12]); Log.InfoFormat("-- Master {0}, HCI_Version {1}, LMP_Version {2}", Local, HciVersion, LmpVersion); if (GlobalConfiguration.Instance.DisableSSP) { Transfered = HCI_Write_Scan_Enable(); } else { Transfered = HCI_Write_Simple_Pairing_Mode(); } } if (Command == HCI.Command.HCI_Write_Simple_Pairing_Mode) { if (Buffer[5] == 0) { Transfered = HCI_Write_Simple_Pairing_Debug_Mode(); } else { GlobalConfiguration.Instance.DisableSSP = true; Log.Warn("-- Simple Pairing not supported on this device. [SSP Disabled]"); Transfered = HCI_Write_Scan_Enable(); } } if (Command == HCI.Command.HCI_Write_Simple_Pairing_Debug_Mode) { Transfered = HCI_Write_Authentication_Enable(); } if (Command == HCI.Command.HCI_Write_Authentication_Enable) { if (Buffer[5] == 0) { Transfered = HCI_Set_Event_Mask(); } else { GlobalConfiguration.Instance.DisableSSP = true; Log.Warn("-- Simple Pairing not supported on this device. [SSP Disabled]"); Transfered = HCI_Write_Scan_Enable(); } } if (Command == HCI.Command.HCI_Set_Event_Mask) { if (Buffer[5] == 0) { Transfered = HCI_Write_Page_Timeout(); } else { GlobalConfiguration.Instance.DisableSSP = true; Log.Warn("-- Simple Pairing not supported on this device. [SSP Disabled]"); Transfered = HCI_Write_Scan_Enable(); } } if (Command == HCI.Command.HCI_Write_Page_Timeout && Buffer[5] == 0) { Transfered = HCI_Write_Page_Scan_Activity(); } if (Command == HCI.Command.HCI_Write_Page_Scan_Activity && Buffer[5] == 0) { Transfered = HCI_Write_Page_Scan_Type(); } if (Command == HCI.Command.HCI_Write_Page_Scan_Type && Buffer[5] == 0) { Transfered = HCI_Write_Inquiry_Scan_Activity(); } if (Command == HCI.Command.HCI_Write_Inquiry_Scan_Activity && Buffer[5] == 0) { Transfered = HCI_Write_Inquiry_Scan_Type(); } if (Command == HCI.Command.HCI_Write_Inquiry_Scan_Type && Buffer[5] == 0) { Transfered = HCI_Write_Inquiry_Mode(); } if (Command == HCI.Command.HCI_Write_Inquiry_Mode && Buffer[5] == 0) { Transfered = HCI_Write_Class_of_Device(); } if (Command == HCI.Command.HCI_Write_Class_of_Device && Buffer[5] == 0) { Transfered = HCI_Write_Extended_Inquiry_Response(); } if (Command == HCI.Command.HCI_Write_Extended_Inquiry_Response && Buffer[5] == 0) { Transfered = HCI_Write_Local_Name(); } if (Command == HCI.Command.HCI_Write_Local_Name && Buffer[5] == 0) { Transfered = HCI_Write_Scan_Enable(); } if (Command == HCI.Command.HCI_Write_Scan_Enable && Buffer[5] == 0) { Initialised = true; } break; case HCI.Event.HCI_Connection_Request_EV: for (var i = 0; i < 6; i++) { BD_Addr[i] = Buffer[i + 2]; } Transfered = HCI_Delete_Stored_Link_Key(BD_Addr); Transfered = HCI_Remote_Name_Request(BD_Addr); break; case HCI.Event.HCI_Connection_Complete_EV: bd = string.Format("{0:X2}:{1:X2}:{2:X2}:{3:X2}:{4:X2}:{5:X2}", Buffer[10], Buffer[9], Buffer[8], Buffer[7], Buffer[6], Buffer[5]); if (!nameList.Any()) { break; } Connection = Add(Buffer[3], (byte)(Buffer[4] | 0x20), nameList[bd]); // TODO: fix workaround, breaks my controller /* if (Buffer[10] != 0x00 || Buffer[9] != 0x07 || Buffer[8] != 0x04) * { * Connection.IsFake = true; * Log.Info("-- Fake DualShock3 found, workaround applied"); * } * else * { * Connection.IsFake = false; * Log.Info("-- Genuine Sony DualShock3 found"); * } */ // fetch configuration from .INI var bdc = IniConfig.Instance.BthDongle; // check if current device matches names or MACs if (bdc.SupportedNames.Any(n => nameList[bd].Contains(n)) || bdc.SupportedMacs.Any(m => bd.StartsWith(m))) { Connection.ServiceByPass = true; } Connection.RemoteName = nameList[bd]; nameList.Remove(bd); Connection.BdAddress = new[] { Buffer[10], Buffer[9], Buffer[8], Buffer[7], Buffer[6], Buffer[5] }; break; case HCI.Event.HCI_Disconnection_Complete_EV: Remove(Buffer[3], (byte)(Buffer[4] | 0x20)); break; case HCI.Event.HCI_Number_Of_Completed_Packets_EV: for (byte Index = 0, Ptr = 3; Index < Buffer[2]; Index++, Ptr += 4) { OnCompletedCount(Buffer[Ptr], (byte)(Buffer[Ptr + 1] | 0x20), (ushort)(Buffer[Ptr + 2] | Buffer[Ptr + 3] << 8)); } break; case HCI.Event.HCI_Remote_Name_Request_Complete_EV: bd = string.Format("{0:X2}:{1:X2}:{2:X2}:{3:X2}:{4:X2}:{5:X2}", Buffer[8], Buffer[7], Buffer[6], Buffer[5], Buffer[4], Buffer[3]); var nm = new StringBuilder(); for (var Index = 9; Index < Buffer.Length; Index++) { if (Buffer[Index] > 0) { nm.Append((char)Buffer[Index]); } else { break; } } var Name = nm.ToString(); Log.InfoFormat("-- Remote Name : {0} - {1}", bd, Name); for (var i = 0; i < 6; i++) { BD_Addr[i] = Buffer[i + 3]; } var hci = IniConfig.Instance.Hci; if (hci.SupportedNames.Any(n => Name.StartsWith(n)) || hci.SupportedNames.Any(n => Name == n)) { nameList.Add(bd, nm.ToString()); Transfered = HCI_Accept_Connection_Request(BD_Addr, 0x00); } else { Transfered = HCI_Reject_Connection_Request(BD_Addr, 0x0F); } break; case HCI.Event.HCI_Link_Key_Request_EV: for (var i = 0; i < 6; i++) { BD_Addr[i] = Buffer[i + 2]; } Transfered = HCI_Link_Key_Request_Reply(BD_Addr); Transfered = HCI_Set_Connection_Encryption(Connection.HciHandle); break; case HCI.Event.HCI_PIN_Code_Request_EV: for (var i = 0; i < 6; i++) { BD_Addr[i] = Buffer[i + 2]; } Transfered = HCI_PIN_Code_Request_Negative_Reply(BD_Addr); break; case HCI.Event.HCI_IO_Capability_Request_EV: Transfered = HCI_IO_Capability_Request_Reply(BD_Addr); break; case HCI.Event.HCI_User_Confirmation_Request_EV: Transfered = HCI_User_Confirmation_Request_Reply(BD_Addr); break; case HCI.Event.HCI_Link_Key_Notification_EV: for (var Index = 0; Index < 6; Index++) { BD_Addr[Index] = Buffer[Index + 2]; } for (var Index = 0; Index < 16; Index++) { BD_Link[Index] = Buffer[Index + 8]; } Transfered = HCI_Set_Connection_Encryption(Connection.HciHandle); break; } } } } catch (Exception ex) { Log.ErrorFormat("Unexpected error in HCI_Worker_Thread: {0}", ex); } } HCI_Reset(); Log.Info("-- Bluetooth : HCI_Worker_Thread Exiting"); }
/// <summary> /// Processes communication with the Bluetooth host device. /// </summary> /// <param name="o">The cancellation token to request task abortion.</param> private void HicWorker(object o) { var token = (CancellationToken)o; var nameList = new SortedDictionary <string, string>(); var hci = IniConfig.Instance.Hci; var bStarted = false; var bd = string.Empty; var buffer = new byte[512]; var bdAddr = new byte[6]; var bdLink = new byte[16]; var bdHandle = new byte[2]; var transfered = 0; var command = HCI.Command.HCI_Null; var connection = new BthConnection(); Log.DebugFormat("Bluetooth Host Controller Interface Task starting on Interrupt Input Pipe: {0:X2}", IntIn); HCI_Reset(); while (!token.IsCancellationRequested) { try { // HCI traffic using the interrupt pipe if (ReadIntPipe(buffer, buffer.Length, ref transfered) && transfered > 0) { if (Enum.IsDefined(typeof(HCI.Event), buffer[0])) { var Event = (HCI.Event)buffer[0]; switch (Event) { case HCI.Event.HCI_Command_Complete_EV: command = (HCI.Command)(ushort) (buffer[3] | buffer[4] << 8); Log.DebugFormat(">> {0} [{1:X2}] [{2:X2}] [{3}]", Event, buffer[0], buffer[5], command); break; case HCI.Event.HCI_Command_Status_EV: command = (HCI.Command)(ushort) (buffer[4] | buffer[5] << 8); Log.DebugFormat(">> {0} [{1:X2}] [{2:X2}] [{3}]", Event, buffer[0], buffer[2], command); if (buffer[2] != 0) { switch (command) { case HCI.Command.HCI_Write_Simple_Pairing_Mode: case HCI.Command.HCI_Write_Authentication_Enable: case HCI.Command.HCI_Set_Event_Mask: GlobalConfiguration.Instance.DisableSSP = true; Log.Warn( "-- Simple Pairing not supported on this device. [SSP Disabled]"); transfered = HCI_Write_Scan_Enable(); break; } } break; case HCI.Event.HCI_Number_Of_Completed_Packets_EV: break; default: Log.DebugFormat(">> {0} [{1:X2}]", Event, buffer[0]); break; } switch (Event) { #region HCI_Command_Complete_EV case HCI.Event.HCI_Command_Complete_EV: if (command == HCI.Command.HCI_Reset && buffer[5] == 0 && !bStarted) { bStarted = true; // TODO: do we really need this? Thread.Sleep(250); transfered = HCI_Read_BD_Addr(); } if (command == HCI.Command.HCI_Read_BD_ADDR && buffer[5] == 0) { BluetoothHostAddress = new PhysicalAddress(new[] { buffer[11], buffer[10], buffer[9], buffer[8], buffer[7], buffer[6] }); transfered = HCI_Read_Buffer_Size(); } if (command == HCI.Command.HCI_Read_Buffer_Size && buffer[5] == 0) { Log.DebugFormat("-- {0:X2}{1:X2}, {2:X2}, {3:X2}{4:X2}, {5:X2}{6:X2}", buffer[7], buffer[6], buffer[8], buffer[10], buffer[9], buffer[12], buffer[11]); transfered = HCI_Read_Local_Version_Info(); } #region Host version // incoming HCI firmware version information if (command == HCI.Command.HCI_Read_Local_Version_Info && buffer[5] == 0) { var hciMajor = buffer[6]; var lmpMajor = buffer[9]; HciVersion = string.Format("{0}.{1:X4}", buffer[6], buffer[8] << 8 | buffer[7]); LmpVersion = string.Format("{0}.{1:X4}", buffer[9], buffer[13] << 8 | buffer[12]); Log.InfoFormat( "Initializing Bluetooth host {0} (HCI-Version: {1}, LMP-Version: {2})", BluetoothHostAddress.AsFriendlyName(), HciVersion, LmpVersion); /* analyzes Host Controller Interface (HCI) major version * see https://www.bluetooth.org/en-us/specification/assigned-numbers/host-controller-interface * */ switch (hciMajor) { case 0: Log.DebugFormat("HCI_Version: Bluetooth® Core Specification 1.0b"); break; case 1: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 1.1"); break; case 2: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 1.2"); break; case 3: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 2.0 + EDR"); break; case 4: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 2.1 + EDR"); break; case 5: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 3.0 + HS"); break; case 6: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 4.0"); break; case 7: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 4.1"); break; case 8: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 4.2"); break; default: // this should not happen Log.ErrorFormat("HCI_Version: Specification unknown"); break; } /* analyzes Link Manager Protocol (LMP) major version * see https://www.bluetooth.org/en-us/specification/assigned-numbers/link-manager * */ switch (lmpMajor) { case 0: Log.DebugFormat("LMP_Version: Bluetooth® Core Specification 1.0b"); break; case 1: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 1.1"); break; case 2: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 1.2"); break; case 3: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 2.0 + EDR"); break; case 4: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 2.1 + EDR"); break; case 5: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 3.0 + HS"); break; case 6: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 4.0"); break; case 7: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 4.1"); break; case 8: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 4.2"); break; default: // this should not happen Log.ErrorFormat("LMP_Version: Specification unknown"); break; } // Bluetooth v2.0 + EDR if (hciMajor >= 3 && lmpMajor >= 3) { Log.InfoFormat( "Bluetooth host supports communication with DualShock 3 controllers"); } // Bluetooth v2.1 + EDR if (hciMajor >= 4 && lmpMajor >= 4) { Log.InfoFormat( "Bluetooth host supports communication with DualShock 4 controllers"); } // dongle effectively too old/unsupported if (hciMajor < 3 || lmpMajor < 3) { Log.FatalFormat( "Unsupported Bluetooth Specification, aborting communication"); transfered = HCI_Reset(); break; } // use simple pairing? if (GlobalConfiguration.Instance.DisableSSP) { transfered = HCI_Write_Scan_Enable(); } else { transfered = HCI_Write_Simple_Pairing_Mode(); } } #endregion if (command == HCI.Command.HCI_Write_Simple_Pairing_Mode) { if (buffer[5] == 0) { transfered = HCI_Write_Simple_Pairing_Debug_Mode(); } else { GlobalConfiguration.Instance.DisableSSP = true; Log.Warn("-- Simple Pairing not supported on this device. [SSP Disabled]"); transfered = HCI_Write_Scan_Enable(); } } if (command == HCI.Command.HCI_Write_Simple_Pairing_Debug_Mode) { transfered = HCI_Write_Authentication_Enable(); } if (command == HCI.Command.HCI_Write_Authentication_Enable) { if (buffer[5] == 0) { transfered = HCI_Set_Event_Mask(); } else { GlobalConfiguration.Instance.DisableSSP = true; Log.Warn("-- Simple Pairing not supported on this device. [SSP Disabled]"); transfered = HCI_Write_Scan_Enable(); } } if (command == HCI.Command.HCI_Set_Event_Mask) { if (buffer[5] == 0) { transfered = HCI_Write_Page_Timeout(); } else { GlobalConfiguration.Instance.DisableSSP = true; Log.Warn("-- Simple Pairing not supported on this device. [SSP Disabled]"); transfered = HCI_Write_Scan_Enable(); } } if (command == HCI.Command.HCI_Write_Page_Timeout && buffer[5] == 0) { transfered = HCI_Write_Page_Scan_Activity(); } if (command == HCI.Command.HCI_Write_Page_Scan_Activity && buffer[5] == 0) { transfered = HCI_Write_Page_Scan_Type(); } if (command == HCI.Command.HCI_Write_Page_Scan_Type && buffer[5] == 0) { transfered = HCI_Write_Inquiry_Scan_Activity(); } if (command == HCI.Command.HCI_Write_Inquiry_Scan_Activity && buffer[5] == 0) { transfered = HCI_Write_Inquiry_Scan_Type(); } if (command == HCI.Command.HCI_Write_Inquiry_Scan_Type && buffer[5] == 0) { transfered = HCI_Write_Inquiry_Mode(); } if (command == HCI.Command.HCI_Write_Inquiry_Mode && buffer[5] == 0) { transfered = HCI_Write_Class_of_Device(); } if (command == HCI.Command.HCI_Write_Class_of_Device && buffer[5] == 0) { transfered = HCI_Write_Extended_Inquiry_Response(); } if (command == HCI.Command.HCI_Write_Extended_Inquiry_Response && buffer[5] == 0) { transfered = HCI_Write_Local_Name(); } if (command == HCI.Command.HCI_Write_Local_Name && buffer[5] == 0) { transfered = HCI_Write_Scan_Enable(); } if (command == HCI.Command.HCI_Write_Scan_Enable && buffer[5] == 0) { Initialised = true; } break; #endregion #region HCI_Connection_Request_EV case HCI.Event.HCI_Connection_Request_EV: Buffer.BlockCopy(buffer, 2, bdAddr, 0, 6); transfered = HCI_Delete_Stored_Link_Key(bdAddr); transfered = HCI_Accept_Connection_Request(bdAddr, 0x00); break; #endregion #region HCI_Connection_Complete_EV case HCI.Event.HCI_Connection_Complete_EV: //buffer[2] contains the status of connection_complete_ev. it's always 0 if succeed if (buffer[2] == 0x00) { Log.DebugFormat("-- HCI_Connection_Complete_EV OK, status: {0:X2}", buffer[2]); //saving the handle for later usage bdHandle[0] = buffer[3]; bdHandle[1] = buffer[4]; _connectionPendingEvent.Reset(); //only after connection completed with status 0 we request for controller's name. transfered = HCI_Remote_Name_Request(bdAddr); } else { Log.WarnFormat( "-- HCI_Connection_Complete_EV failed with status: {0:X2}. Connection handle:0x{1:X2}{2:X2}", buffer[2], buffer[4], buffer[3]); // TODO: you might want to add some other command here to break or retry. } break; #endregion #region HCI_Disconnection_Complete_EV case HCI.Event.HCI_Disconnection_Complete_EV: Remove(buffer[3], (byte)(buffer[4] | 0x20)); break; #endregion #region HCI_Number_Of_Completed_Packets_EV case HCI.Event.HCI_Number_Of_Completed_Packets_EV: for (byte index = 0, ptr = 3; index < buffer[2]; index++, ptr += 4) { OnCompletedCount(buffer[ptr], (byte)(buffer[ptr + 1] | 0x20), (ushort)(buffer[ptr + 2] | buffer[ptr + 3] << 8)); } break; #endregion #region HCI_Remote_Name_Request_Complete_EV case HCI.Event.HCI_Remote_Name_Request_Complete_EV: bd = string.Format("{0:X2}:{1:X2}:{2:X2}:{3:X2}:{4:X2}:{5:X2}", buffer[8], buffer[7], buffer[6], buffer[5], buffer[4], buffer[3]); var nm = new StringBuilder(); // extract product name for (var index = 9; index < buffer.Length; index++) { if (buffer[index] > 0) { nm.Append((char)buffer[index]); } else { break; } } var name = nm.ToString(); Log.DebugFormat("-- Remote Name : {0} - {1}", bd, name); // extract MAC address Buffer.BlockCopy(buffer, 3, bdAddr, 0, 6); if (hci.SupportedNames.Any(n => name.StartsWith(n)) || hci.SupportedNames.Any(n => name == n)) { nameList.Add(bd, name); // the code below is just cut-paste from "case HCI.Event.HCI_Connection_Complete_EV" // just some adjustments made in the buffer variables if (!nameList.Any()) { break; } //using there the handles saved earlier connection = Add(bdHandle[0], (byte)(bdHandle[1] | 0x20), nameList[bd]); #region Fake DS3 workaround // skip fake check for version 4 controllers if (!name.Equals(BthDs4.GenuineProductName, StringComparison.OrdinalIgnoreCase)) { if (!hci.GenuineMacAddresses.Any(m => bd.StartsWith(m))) { connection.IsFake = true; Log.Warn("Fake DualShock 3 found. Trying Workarounds..."); } else { connection.IsFake = false; Log.Info("Genuine Sony DualShock 3 found"); } } else { Log.Info("Sony DualShock 4 found"); } #endregion connection.RemoteName = nameList[bd]; nameList.Remove(bd); connection.DeviceAddress = new PhysicalAddress(new[] { buffer[8], buffer[7], buffer[6], buffer[5], buffer[4], buffer[3] }); _connectionPendingEvent.Set(); } else { transfered = HCI_Reject_Connection_Request(bdAddr, 0x0F); } break; #endregion #region HCI_Link_Key_Request_EV case HCI.Event.HCI_Link_Key_Request_EV: Buffer.BlockCopy(buffer, 2, bdAddr, 0, 6); transfered = HCI_Link_Key_Request_Reply(bdAddr); transfered = HCI_Set_Connection_Encryption(connection.HciHandle); break; #endregion #region HCI_PIN_Code_Request_EV case HCI.Event.HCI_PIN_Code_Request_EV: Buffer.BlockCopy(buffer, 2, bdAddr, 0, 6); transfered = HCI_PIN_Code_Request_Negative_Reply(bdAddr); break; #endregion #region HCI_IO_Capability_Request_EV case HCI.Event.HCI_IO_Capability_Request_EV: transfered = HCI_IO_Capability_Request_Reply(bdAddr); break; #endregion #region HCI_User_Confirmation_Request_EV case HCI.Event.HCI_User_Confirmation_Request_EV: transfered = HCI_User_Confirmation_Request_Reply(bdAddr); break; #endregion #region HCI_Link_Key_Notification_EV case HCI.Event.HCI_Link_Key_Notification_EV: Buffer.BlockCopy(buffer, 2, bdAddr, 0, 6); Buffer.BlockCopy(buffer, 8, bdLink, 0, 16); transfered = HCI_Set_Connection_Encryption(connection.HciHandle); break; #endregion } } } } catch (Exception ex) { Log.ErrorFormat("Unexpected error in HCI_Worker_Thread: {0}", ex); } } HCI_Reset(); Log.Debug("-- Bluetooth : HCI_Worker_Thread Exiting"); }
/// <summary> /// Processes communication with the Bluetooth host device. /// </summary> /// <param name="o">The cancellation token to request task abortion.</param> private void HicWorker(object o) { var token = (CancellationToken) o; var nameList = new SortedDictionary<string, string>(); var hci = IniConfig.Instance.Hci; var bStarted = false; var bd = string.Empty; var buffer = new byte[512]; var bdAddr = new byte[6]; var bdLink = new byte[16]; var bdHandle = new byte[2]; var transfered = 0; var command = HCI.Command.HCI_Null; var connection = new BthConnection(); Log.InfoFormat("-- Bluetooth : HCI_Worker_Thread Starting (IN: {0:X2})", IntIn); HCI_Reset(); while (!token.IsCancellationRequested) { try { // HCI traffic using the interrupt pipe if (ReadIntPipe(buffer, buffer.Length, ref transfered) && transfered > 0) { if (Enum.IsDefined(typeof (HCI.Event), buffer[0])) { var Event = (HCI.Event) buffer[0]; switch (Event) { case HCI.Event.HCI_Command_Complete_EV: command = (HCI.Command) (ushort) (buffer[3] | buffer[4] << 8); Log.DebugFormat(">> {0} [{1:X2}] [{2:X2}] [{3}]", Event, buffer[0], buffer[5], command); break; case HCI.Event.HCI_Command_Status_EV: command = (HCI.Command) (ushort) (buffer[4] | buffer[5] << 8); Log.DebugFormat(">> {0} [{1:X2}] [{2:X2}] [{3}]", Event, buffer[0], buffer[2], command); if (buffer[2] != 0) { switch (command) { case HCI.Command.HCI_Write_Simple_Pairing_Mode: case HCI.Command.HCI_Write_Authentication_Enable: case HCI.Command.HCI_Set_Event_Mask: GlobalConfiguration.Instance.DisableSSP = true; Log.Warn( "-- Simple Pairing not supported on this device. [SSP Disabled]"); transfered = HCI_Write_Scan_Enable(); break; } } break; case HCI.Event.HCI_Number_Of_Completed_Packets_EV: break; default: Log.DebugFormat(">> {0} [{1:X2}]", Event, buffer[0]); break; } switch (Event) { #region HCI_Command_Complete_EV case HCI.Event.HCI_Command_Complete_EV: if (command == HCI.Command.HCI_Reset && buffer[5] == 0 && !bStarted) { bStarted = true; // TODO: do we really need this? Thread.Sleep(250); transfered = HCI_Read_BD_Addr(); } if (command == HCI.Command.HCI_Read_BD_ADDR && buffer[5] == 0) { _localMac = new[] {buffer[6], buffer[7], buffer[8], buffer[9], buffer[10], buffer[11]}; transfered = HCI_Read_Buffer_Size(); } if (command == HCI.Command.HCI_Read_Buffer_Size && buffer[5] == 0) { Log.DebugFormat("-- {0:X2}{1:X2}, {2:X2}, {3:X2}{4:X2}, {5:X2}{6:X2}", buffer[7], buffer[6], buffer[8], buffer[10], buffer[9], buffer[12], buffer[11]); transfered = HCI_Read_Local_Version_Info(); } #region Host version // incoming HCI firmware version information if (command == HCI.Command.HCI_Read_Local_Version_Info && buffer[5] == 0) { var hciMajor = buffer[6]; var lmpMajor = buffer[9]; HciVersion = string.Format("{0}.{1:X4}", buffer[6], buffer[8] << 8 | buffer[7]); LmpVersion = string.Format("{0}.{1:X4}", buffer[9], buffer[13] << 8 | buffer[12]); Log.InfoFormat("-- Master {0}, HCI_Version {1}, LMP_Version {2}", Local, HciVersion, LmpVersion); /* analyzes Host Controller Interface (HCI) major version * see https://www.bluetooth.org/en-us/specification/assigned-numbers/host-controller-interface * */ switch (hciMajor) { case 0: Log.DebugFormat("HCI_Version: Bluetooth® Core Specification 1.0b"); break; case 1: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 1.1"); break; case 2: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 1.2"); break; case 3: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 2.0 + EDR"); break; case 4: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 2.1 + EDR"); break; case 5: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 3.0 + HS"); break; case 6: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 4.0"); break; case 7: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 4.1"); break; case 8: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 4.2"); break; default: // this should not happen Log.ErrorFormat("HCI_Version: Specification unknown"); break; } /* analyzes Link Manager Protocol (LMP) major version * see https://www.bluetooth.org/en-us/specification/assigned-numbers/link-manager * */ switch (lmpMajor) { case 0: Log.DebugFormat("LMP_Version: Bluetooth® Core Specification 1.0b"); break; case 1: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 1.1"); break; case 2: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 1.2"); break; case 3: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 2.0 + EDR"); break; case 4: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 2.1 + EDR"); break; case 5: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 3.0 + HS"); break; case 6: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 4.0"); break; case 7: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 4.1"); break; case 8: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 4.2"); break; default: // this should not happen Log.ErrorFormat("LMP_Version: Specification unknown"); break; } // Bluetooth v2.0 + EDR if (hciMajor >= 3 && lmpMajor >= 3) { Log.InfoFormat( "Bluetooth host supports communication with DualShock 3 controllers"); } // Bluetooth v2.1 + EDR if (hciMajor >= 4 && lmpMajor >= 4) { Log.InfoFormat( "Bluetooth host supports communication with DualShock 4 controllers"); } // dongle effectively too old/unsupported if (hciMajor < 3 || lmpMajor < 3) { Log.FatalFormat( "Unsupported Bluetooth Specification, aborting communication"); transfered = HCI_Reset(); break; } // use simple pairing? if (GlobalConfiguration.Instance.DisableSSP) { transfered = HCI_Write_Scan_Enable(); } else { transfered = HCI_Write_Simple_Pairing_Mode(); } } #endregion if (command == HCI.Command.HCI_Write_Simple_Pairing_Mode) { if (buffer[5] == 0) { transfered = HCI_Write_Simple_Pairing_Debug_Mode(); } else { GlobalConfiguration.Instance.DisableSSP = true; Log.Warn("-- Simple Pairing not supported on this device. [SSP Disabled]"); transfered = HCI_Write_Scan_Enable(); } } if (command == HCI.Command.HCI_Write_Simple_Pairing_Debug_Mode) { transfered = HCI_Write_Authentication_Enable(); } if (command == HCI.Command.HCI_Write_Authentication_Enable) { if (buffer[5] == 0) { transfered = HCI_Set_Event_Mask(); } else { GlobalConfiguration.Instance.DisableSSP = true; Log.Warn("-- Simple Pairing not supported on this device. [SSP Disabled]"); transfered = HCI_Write_Scan_Enable(); } } if (command == HCI.Command.HCI_Set_Event_Mask) { if (buffer[5] == 0) { transfered = HCI_Write_Page_Timeout(); } else { GlobalConfiguration.Instance.DisableSSP = true; Log.Warn("-- Simple Pairing not supported on this device. [SSP Disabled]"); transfered = HCI_Write_Scan_Enable(); } } if (command == HCI.Command.HCI_Write_Page_Timeout && buffer[5] == 0) { transfered = HCI_Write_Page_Scan_Activity(); } if (command == HCI.Command.HCI_Write_Page_Scan_Activity && buffer[5] == 0) { transfered = HCI_Write_Page_Scan_Type(); } if (command == HCI.Command.HCI_Write_Page_Scan_Type && buffer[5] == 0) { transfered = HCI_Write_Inquiry_Scan_Activity(); } if (command == HCI.Command.HCI_Write_Inquiry_Scan_Activity && buffer[5] == 0) { transfered = HCI_Write_Inquiry_Scan_Type(); } if (command == HCI.Command.HCI_Write_Inquiry_Scan_Type && buffer[5] == 0) { transfered = HCI_Write_Inquiry_Mode(); } if (command == HCI.Command.HCI_Write_Inquiry_Mode && buffer[5] == 0) { transfered = HCI_Write_Class_of_Device(); } if (command == HCI.Command.HCI_Write_Class_of_Device && buffer[5] == 0) { transfered = HCI_Write_Extended_Inquiry_Response(); } if (command == HCI.Command.HCI_Write_Extended_Inquiry_Response && buffer[5] == 0) { transfered = HCI_Write_Local_Name(); } if (command == HCI.Command.HCI_Write_Local_Name && buffer[5] == 0) { transfered = HCI_Write_Scan_Enable(); } if (command == HCI.Command.HCI_Write_Scan_Enable && buffer[5] == 0) { Initialised = true; } break; #endregion #region HCI_Connection_Request_EV case HCI.Event.HCI_Connection_Request_EV: Buffer.BlockCopy(buffer, 2, bdAddr, 0, 6); transfered = HCI_Delete_Stored_Link_Key(bdAddr); transfered = HCI_Accept_Connection_Request(bdAddr, 0x00); _waitForConnectionComplete.Reset(); break; #endregion #region HCI_Connection_Complete_EV case HCI.Event.HCI_Connection_Complete_EV: //buffer[2] contains the status of connection_complete_ev. it's always 0 if succeed if (buffer[2] == 0x00) { Log.InfoFormat("-- HCI_Connection_Complete_EV OK, status: {0:X2}", buffer[2]); //saving the handle for later usage bdHandle[0] = buffer[3]; bdHandle[1] = buffer[4]; //only after connection completed with status 0 we request for controller's name. transfered = HCI_Remote_Name_Request(bdAddr); } else { Log.WarnFormat( "-- HCI_Connection_Complete_EV failed with status: {0:X2}. Connection handle:0x{1:X2}{2:X2}", buffer[2], buffer[4], buffer[3]); // TODO: you might want to add some other command here to break or retry. } break; #endregion #region HCI_Disconnection_Complete_EV case HCI.Event.HCI_Disconnection_Complete_EV: Remove(buffer[3], (byte) (buffer[4] | 0x20)); break; #endregion #region HCI_Number_Of_Completed_Packets_EV case HCI.Event.HCI_Number_Of_Completed_Packets_EV: for (byte index = 0, ptr = 3; index < buffer[2]; index++, ptr += 4) { OnCompletedCount(buffer[ptr], (byte) (buffer[ptr + 1] | 0x20), (ushort) (buffer[ptr + 2] | buffer[ptr + 3] << 8)); } break; #endregion #region HCI_Remote_Name_Request_Complete_EV case HCI.Event.HCI_Remote_Name_Request_Complete_EV: bd = string.Format("{0:X2}:{1:X2}:{2:X2}:{3:X2}:{4:X2}:{5:X2}", buffer[8], buffer[7], buffer[6], buffer[5], buffer[4], buffer[3]); var nm = new StringBuilder(); // extract product name for (var index = 9; index < buffer.Length; index++) { if (buffer[index] > 0) nm.Append((char) buffer[index]); else break; } var name = nm.ToString(); Log.InfoFormat("-- Remote Name : {0} - {1}", bd, name); // extract MAC address Buffer.BlockCopy(buffer, 3, bdAddr, 0, 6); if (hci.SupportedNames.Any(n => name.StartsWith(n)) || hci.SupportedNames.Any(n => name == n)) { nameList.Add(bd, nm.ToString()); // the code below is just cut-paste from "case HCI.Event.HCI_Connection_Complete_EV" // just some adjustments made in the buffer variables if (!nameList.Any()) break; connection = Add(bdHandle[0], (byte) (bdHandle[1] | 0x20), nameList[bd]); //using there the handles saved earlier // signal L2CAP task to continue _waitForConnectionComplete.Set(); #region Fake DS3 workaround if (GlobalConfiguration.Instance.UseDs3CounterfeitWorkarounds && !hci.GenuineMacAddresses.Any(m => bd.StartsWith(m))) { connection.IsFake = true; Log.Warn("-- Fake DualShock 3 found. Workaround applied"); } else { connection.IsFake = false; Log.Info("-- Genuine Sony DualShock 3 found"); } #endregion connection.RemoteName = nameList[bd]; nameList.Remove(bd); connection.BdAddress = new[] {buffer[8], buffer[7], buffer[6], buffer[5], buffer[4], buffer[3]}; } else { transfered = HCI_Reject_Connection_Request(bdAddr, 0x0F); } break; #endregion #region HCI_Link_Key_Request_EV case HCI.Event.HCI_Link_Key_Request_EV: Buffer.BlockCopy(buffer, 2, bdAddr, 0, 6); transfered = HCI_Link_Key_Request_Reply(bdAddr); transfered = HCI_Set_Connection_Encryption(connection.HciHandle); break; #endregion #region HCI_PIN_Code_Request_EV case HCI.Event.HCI_PIN_Code_Request_EV: Buffer.BlockCopy(buffer, 2, bdAddr, 0, 6); transfered = HCI_PIN_Code_Request_Negative_Reply(bdAddr); break; #endregion #region HCI_IO_Capability_Request_EV case HCI.Event.HCI_IO_Capability_Request_EV: transfered = HCI_IO_Capability_Request_Reply(bdAddr); break; #endregion #region HCI_User_Confirmation_Request_EV case HCI.Event.HCI_User_Confirmation_Request_EV: transfered = HCI_User_Confirmation_Request_Reply(bdAddr); break; #endregion #region HCI_Link_Key_Notification_EV case HCI.Event.HCI_Link_Key_Notification_EV: Buffer.BlockCopy(buffer, 2, bdAddr, 0, 6); Buffer.BlockCopy(buffer, 8, bdLink, 0, 16); transfered = HCI_Set_Connection_Encryption(connection.HciHandle); break; #endregion } } } } catch (Exception ex) { Log.ErrorFormat("Unexpected error in HCI_Worker_Thread: {0}", ex); } } HCI_Reset(); Log.Info("-- Bluetooth : HCI_Worker_Thread Exiting"); }
private void HicWorker(object o) { var token = (CancellationToken)o; var nameList = new SortedDictionary<string, string>(); var hci = IniConfig.Instance.Hci; var bStarted = false; var bd = string.Empty; var Buffer = new byte[512]; var BD_Addr = new byte[6]; var BD_Link = new byte[16]; var Transfered = 0; HCI.Event Event; var Command = HCI.Command.HCI_Null; var Connection = new BthConnection(); Log.InfoFormat("-- Bluetooth : HCI_Worker_Thread Starting (IN: {0:X2})", IntIn); HCI_Reset(); while (!token.IsCancellationRequested) { try { if (ReadIntPipe(Buffer, Buffer.Length, ref Transfered) && Transfered > 0) { if (Enum.IsDefined(typeof(HCI.Event), Buffer[0])) { Event = (HCI.Event)Buffer[0]; switch (Event) { case HCI.Event.HCI_Command_Complete_EV: Command = (HCI.Command)(ushort)(Buffer[3] | Buffer[4] << 8); Log.DebugFormat(">> {0} [{1:X2}] [{2:X2}] [{3}]", Event, Buffer[0], Buffer[5], Command); break; case HCI.Event.HCI_Command_Status_EV: Command = (HCI.Command)(ushort)(Buffer[4] | Buffer[5] << 8); Log.DebugFormat(">> {0} [{1:X2}] [{2:X2}] [{3}]", Event, Buffer[0], Buffer[2], Command); if (Buffer[2] != 0) { switch (Command) { case HCI.Command.HCI_Write_Simple_Pairing_Mode: case HCI.Command.HCI_Write_Authentication_Enable: case HCI.Command.HCI_Set_Event_Mask: GlobalConfiguration.Instance.DisableSSP = true; Log.Warn( "-- Simple Pairing not supported on this device. [SSP Disabled]"); Transfered = HCI_Write_Scan_Enable(); break; } } break; case HCI.Event.HCI_Number_Of_Completed_Packets_EV: break; default: Log.DebugFormat(">> {0} [{1:X2}]", Event, Buffer[0]); break; } switch (Event) { case HCI.Event.HCI_Command_Complete_EV: if (Command == HCI.Command.HCI_Reset && Buffer[5] == 0 && !bStarted) { bStarted = true; Thread.Sleep(250); Transfered = HCI_Read_BD_Addr(); } if (Command == HCI.Command.HCI_Read_BD_ADDR && Buffer[5] == 0) { _localMac = new[] { Buffer[6], Buffer[7], Buffer[8], Buffer[9], Buffer[10], Buffer[11] }; Transfered = HCI_Read_Buffer_Size(); } if (Command == HCI.Command.HCI_Read_Buffer_Size && Buffer[5] == 0) { Log.DebugFormat("-- {0:X2}{1:X2}, {2:X2}, {3:X2}{4:X2}, {5:X2}{6:X2}", Buffer[7], Buffer[6], Buffer[8], Buffer[10], Buffer[9], Buffer[12], Buffer[11]); Transfered = HCI_Read_Local_Version_Info(); } // incoming HCI firmware version information if (Command == HCI.Command.HCI_Read_Local_Version_Info && Buffer[5] == 0) { var hciMajor = Buffer[6]; var lmpMajor = Buffer[9]; HciVersion = string.Format("{0}.{1:X4}", Buffer[6], Buffer[8] << 8 | Buffer[7]); LmpVersion = string.Format("{0}.{1:X4}", Buffer[9], Buffer[13] << 8 | Buffer[12]); Log.InfoFormat("-- Master {0}, HCI_Version {1}, LMP_Version {2}", Local, HciVersion, LmpVersion); /* analyzes Host Controller Interface (HCI) major version * see https://www.bluetooth.org/en-us/specification/assigned-numbers/host-controller-interface * */ switch (hciMajor) { case 0: Log.DebugFormat("HCI_Version: Bluetooth® Core Specification 1.0b"); break; case 1: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 1.1"); break; case 2: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 1.2"); break; case 3: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 2.0 + EDR"); break; case 4: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 2.1 + EDR"); break; case 5: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 3.0 + HS"); break; case 6: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 4.0"); break; case 7: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 4.1"); break; case 8: Log.DebugFormat("HCI_Version: Bluetooth Core Specification 4.2"); break; default: // this should not happen Log.ErrorFormat("HCI_Version: Specification unknown"); break; } /* analyzes Link Manager Protocol (LMP) major version * see https://www.bluetooth.org/en-us/specification/assigned-numbers/link-manager * */ switch (lmpMajor) { case 0: Log.DebugFormat("LMP_Version: Bluetooth® Core Specification 1.0b"); break; case 1: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 1.1"); break; case 2: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 1.2"); break; case 3: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 2.0 + EDR"); break; case 4: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 2.1 + EDR"); break; case 5: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 3.0 + HS"); break; case 6: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 4.0"); break; case 7: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 4.1"); break; case 8: Log.DebugFormat("LMP_Version: Bluetooth Core Specification 4.2"); break; default: // this should not happen Log.ErrorFormat("LMP_Version: Specification unknown"); break; } // Bluetooth v2.0 + EDR if (hciMajor >= 3 && lmpMajor >= 3) { Log.InfoFormat("Bluetooth host supports communication with DualShock 3 controllers"); } // Bluetooth v2.1 + EDR if (hciMajor >= 4 && lmpMajor >= 4) { Log.InfoFormat("Bluetooth host supports communication with DualShock 4 controllers"); } // dongle effectively too old/unsupported if (hciMajor < 3 || lmpMajor < 3) { Log.FatalFormat("Unsupported Bluetooth Specification, aborting communication"); Transfered = HCI_Reset(); break; } // use simple pairing? if (GlobalConfiguration.Instance.DisableSSP) { Transfered = HCI_Write_Scan_Enable(); } else { Transfered = HCI_Write_Simple_Pairing_Mode(); } } if (Command == HCI.Command.HCI_Write_Simple_Pairing_Mode) { if (Buffer[5] == 0) { Transfered = HCI_Write_Simple_Pairing_Debug_Mode(); } else { GlobalConfiguration.Instance.DisableSSP = true; Log.Warn("-- Simple Pairing not supported on this device. [SSP Disabled]"); Transfered = HCI_Write_Scan_Enable(); } } if (Command == HCI.Command.HCI_Write_Simple_Pairing_Debug_Mode) { Transfered = HCI_Write_Authentication_Enable(); } if (Command == HCI.Command.HCI_Write_Authentication_Enable) { if (Buffer[5] == 0) { Transfered = HCI_Set_Event_Mask(); } else { GlobalConfiguration.Instance.DisableSSP = true; Log.Warn("-- Simple Pairing not supported on this device. [SSP Disabled]"); Transfered = HCI_Write_Scan_Enable(); } } if (Command == HCI.Command.HCI_Set_Event_Mask) { if (Buffer[5] == 0) { Transfered = HCI_Write_Page_Timeout(); } else { GlobalConfiguration.Instance.DisableSSP = true; Log.Warn("-- Simple Pairing not supported on this device. [SSP Disabled]"); Transfered = HCI_Write_Scan_Enable(); } } if (Command == HCI.Command.HCI_Write_Page_Timeout && Buffer[5] == 0) { Transfered = HCI_Write_Page_Scan_Activity(); } if (Command == HCI.Command.HCI_Write_Page_Scan_Activity && Buffer[5] == 0) { Transfered = HCI_Write_Page_Scan_Type(); } if (Command == HCI.Command.HCI_Write_Page_Scan_Type && Buffer[5] == 0) { Transfered = HCI_Write_Inquiry_Scan_Activity(); } if (Command == HCI.Command.HCI_Write_Inquiry_Scan_Activity && Buffer[5] == 0) { Transfered = HCI_Write_Inquiry_Scan_Type(); } if (Command == HCI.Command.HCI_Write_Inquiry_Scan_Type && Buffer[5] == 0) { Transfered = HCI_Write_Inquiry_Mode(); } if (Command == HCI.Command.HCI_Write_Inquiry_Mode && Buffer[5] == 0) { Transfered = HCI_Write_Class_of_Device(); } if (Command == HCI.Command.HCI_Write_Class_of_Device && Buffer[5] == 0) { Transfered = HCI_Write_Extended_Inquiry_Response(); } if (Command == HCI.Command.HCI_Write_Extended_Inquiry_Response && Buffer[5] == 0) { Transfered = HCI_Write_Local_Name(); } if (Command == HCI.Command.HCI_Write_Local_Name && Buffer[5] == 0) { Transfered = HCI_Write_Scan_Enable(); } if (Command == HCI.Command.HCI_Write_Scan_Enable && Buffer[5] == 0) { Initialised = true; } break; case HCI.Event.HCI_Connection_Request_EV: for (var i = 0; i < 6; i++) BD_Addr[i] = Buffer[i + 2]; Transfered = HCI_Delete_Stored_Link_Key(BD_Addr); Transfered = HCI_Remote_Name_Request(BD_Addr); break; case HCI.Event.HCI_Connection_Complete_EV: bd = string.Format("{0:X2}:{1:X2}:{2:X2}:{3:X2}:{4:X2}:{5:X2}", Buffer[10], Buffer[9], Buffer[8], Buffer[7], Buffer[6], Buffer[5]); if (!nameList.Any()) break; Connection = Add(Buffer[3], (byte)(Buffer[4] | 0x20), nameList[bd]); #region Fake DS3 workaround if (!hci.GenuineMacAddresses.Any(m => bd.StartsWith(m))) { Connection.IsFake = true; Log.Warn("-- Fake DualShock 3 found. Workaround applied"); } else { Connection.IsFake = false; Log.Info("-- Genuine Sony DualShock 3 found"); } #endregion Connection.RemoteName = nameList[bd]; nameList.Remove(bd); Connection.BdAddress = new[] { Buffer[10], Buffer[9], Buffer[8], Buffer[7], Buffer[6], Buffer[5] }; break; case HCI.Event.HCI_Disconnection_Complete_EV: Remove(Buffer[3], (byte)(Buffer[4] | 0x20)); break; case HCI.Event.HCI_Number_Of_Completed_Packets_EV: for (byte Index = 0, Ptr = 3; Index < Buffer[2]; Index++, Ptr += 4) { OnCompletedCount(Buffer[Ptr], (byte)(Buffer[Ptr + 1] | 0x20), (ushort)(Buffer[Ptr + 2] | Buffer[Ptr + 3] << 8)); } break; case HCI.Event.HCI_Remote_Name_Request_Complete_EV: bd = string.Format("{0:X2}:{1:X2}:{2:X2}:{3:X2}:{4:X2}:{5:X2}", Buffer[8], Buffer[7], Buffer[6], Buffer[5], Buffer[4], Buffer[3]); var nm = new StringBuilder(); for (var Index = 9; Index < Buffer.Length; Index++) { if (Buffer[Index] > 0) nm.Append((char)Buffer[Index]); else break; } var Name = nm.ToString(); Log.InfoFormat("-- Remote Name : {0} - {1}", bd, Name); for (var i = 0; i < 6; i++) BD_Addr[i] = Buffer[i + 3]; if (hci.SupportedNames.Any(n => Name.StartsWith(n)) || hci.SupportedNames.Any(n => Name == n)) { nameList.Add(bd, nm.ToString()); Transfered = HCI_Accept_Connection_Request(BD_Addr, 0x00); } else { Transfered = HCI_Reject_Connection_Request(BD_Addr, 0x0F); } break; case HCI.Event.HCI_Link_Key_Request_EV: for (var i = 0; i < 6; i++) BD_Addr[i] = Buffer[i + 2]; Transfered = HCI_Link_Key_Request_Reply(BD_Addr); Transfered = HCI_Set_Connection_Encryption(Connection.HciHandle); break; case HCI.Event.HCI_PIN_Code_Request_EV: for (var i = 0; i < 6; i++) BD_Addr[i] = Buffer[i + 2]; Transfered = HCI_PIN_Code_Request_Negative_Reply(BD_Addr); break; case HCI.Event.HCI_IO_Capability_Request_EV: Transfered = HCI_IO_Capability_Request_Reply(BD_Addr); break; case HCI.Event.HCI_User_Confirmation_Request_EV: Transfered = HCI_User_Confirmation_Request_Reply(BD_Addr); break; case HCI.Event.HCI_Link_Key_Notification_EV: for (var Index = 0; Index < 6; Index++) BD_Addr[Index] = Buffer[Index + 2]; for (var Index = 0; Index < 16; Index++) BD_Link[Index] = Buffer[Index + 8]; Transfered = HCI_Set_Connection_Encryption(Connection.HciHandle); break; } } } } catch (Exception ex) { Log.ErrorFormat("Unexpected error in HCI_Worker_Thread: {0}", ex); } } HCI_Reset(); Log.Info("-- Bluetooth : HCI_Worker_Thread Exiting"); }