/// <summary> /// Send a rumble request to the controller. /// </summary> /// <param name="large">Rumble with large (left) motor.</param> /// <param name="small">Rumble with small (right) motor.</param> /// <returns></returns> public override bool Rumble(byte large, byte small) { lock (_hidOutputReport) { if (GlobalConfiguration.Instance.DisableRumble) { _hidOutputReport[4] = 0; _hidOutputReport[6] = 0; } else { _hidOutputReport[4] = (byte)(small > 0 ? 0x01 : 0x00); _hidOutputReport[6] = large; } if (!m_Blocked && GlobalConfiguration.Instance.Latency == 0) { m_Last = DateTime.Now; m_Blocked = true; BluetoothDevice.HID_Command(HciHandle.Bytes, Get_SCID(L2CAP.PSM.HID_Command), _hidOutputReport); } else { m_Queued = 1; } } return(true); }
/// <summary> /// Send Rumble request to controller. /// </summary> /// <param name="large">Larg motor.</param> /// <param name="small">Small motor.</param> /// <returns>Always true.</returns> public override bool Rumble(byte large, byte small) { lock (_hidReport) { if (GlobalConfiguration.Instance.DisableRumble) { _hidReport[7] = 0; _hidReport[8] = 0; } else { _hidReport[7] = small; _hidReport[8] = large; } if (!m_Blocked) { m_Last = DateTime.Now; m_Blocked = true; BluetoothDevice.HID_Command(HciHandle.Bytes, Get_SCID(L2CAP.PSM.HID_Command), _hidReport); } else { m_Queued = 1; } } return(true); }
public override bool Start() { CanStartHid = false; State = DsState.Connected; m_Queued = 1; m_Blocked = true; m_Last = DateTime.Now; BluetoothDevice.HID_Command(HciHandle.Bytes, Get_SCID(L2CAP.PSM.HID_Command), _hidCommandEnable); return(base.Start()); }
public override bool Start() { CanStartHid = false; State = DsState.Reserved; _hidReport[2] = (byte)GlobalConfiguration.Instance.Ds4InputUpdateDelay; _calGetTries = 0; m_Last = DateTime.Now; BluetoothDevice.HID_Command(HciHandle.Bytes, Get_SCID(L2CAP.PSM.HID_Command), _getCalFeatureReport); return(true); }
/// <summary> /// Send a rumble request to the controller. /// </summary> /// <param name="large">Rumble with large (left) motor.</param> /// <param name="small">Rumble with small (right) motor.</param> /// <returns></returns> public override bool Rumble(byte large, byte small) { lock (_hidOutputReport) { //zero out marker and value bytes for rumble first _hidOutputReport[3] = 0; _hidOutputReport[4] = 0; _hidOutputReport[5] = 0; _hidOutputReport[6] = 0; if (_cal != null) { _cal.ApplyCalToOutReport(_hidOutputReport, 2); } if (_hidOutputReport[5] != 0xFF) //if not already used for cal { //set marker bytes for rumble _hidOutputReport[3] = 0xFF; _hidOutputReport[5] = 0xFF; if (GlobalConfiguration.Instance.DisableRumble) { _hidOutputReport[4] = 0; _hidOutputReport[6] = 0; } else { _hidOutputReport[4] = (byte)(small > 0 ? 0x01 : 0x00); _hidOutputReport[6] = large; } } _hidOutputReport[11] = _ledStatus; if (!m_Blocked && GlobalConfiguration.Instance.Latency == 0) { m_Last = DateTime.Now; m_Blocked = true; BluetoothDevice.HID_Command(HciHandle.Bytes, Get_SCID(L2CAP.PSM.HID_Command), _hidOutputReport); } else { m_Queued = 1; } } return(true); }
public override bool InitHidReport(byte[] report) { var retVal = false; if (m_Init < _hidInitReport.Length) { BluetoothDevice.HID_Command(HciHandle.Bytes, Get_SCID(L2CAP.PSM.HID_Service), _hidInitReport[m_Init++]); } else if (m_Init == _hidInitReport.Length) { m_Init++; retVal = true; } return(retVal); }
public override bool Start() { CanStartHid = false; State = DsState.Connected; byte[] eepromContents = null, statusContents = null; using (var db = new ScpDb()) { using (var tran = db.Engine.GetTransaction()) { var row = tran.Select <byte[], byte[]>(ScpDb.TableDS3Data, DeviceAddress.GetAddressBytes()); if (row.Exists) { eepromContents = new byte[49]; statusContents = new byte[49]; var rowData = row.Value; Array.Copy(rowData, 0, eepromContents, 0, eepromContents.Length); Array.Copy(rowData, eepromContents.Length, statusContents, 0, statusContents.Length); } } } if (eepromContents != null && statusContents != null) { _cal = new DS3CalInstance(DeviceAddress, eepromContents); _cal.InitialCal(statusContents); } if (!IsFake && _cal == null) { Log.WarnFormat("EEPROM data for DS3 controller {0} not present, please connect it via USB first!", DeviceAddress.AsFriendlyName()); } m_Queued = 1; m_Blocked = true; m_Last = DateTime.Now; BluetoothDevice.HID_Command(HciHandle.Bytes, Get_SCID(L2CAP.PSM.HID_Command), _hidCommandEnable); return(base.Start()); }
protected override void Process(DateTime now) { if (!Monitor.TryEnter(_hidReport) || State != DsState.Connected) { return; } try { #region Light bar manipulation if (!GlobalConfiguration.Instance.IsLightBarDisabled) { if (Battery < DsBattery.Medium) { if (!_flash) { _hidReport[12] = _hidReport[13] = 0x40; _flash = true; m_Queued = 1; } } else { if (_flash) { _hidReport[12] = _hidReport[13] = 0x00; _flash = false; m_Queued = 1; } } } if (GlobalConfiguration.Instance.Brightness != _brightness) { _brightness = GlobalConfiguration.Instance.Brightness; } if (XInputSlot.HasValue) { SetLightBarColor((DsPadId)XInputSlot); } #endregion if ((now - m_Last).TotalMilliseconds >= 500) { if (_hidReport[7] > 0x00 || _hidReport[8] > 0x00) { m_Queued = 1; } } if (!m_Blocked && m_Queued > 0) { m_Last = now; m_Blocked = true; m_Queued--; BluetoothDevice.HID_Command(HciHandle.Bytes, Get_SCID(L2CAP.PSM.HID_Command), _hidReport); } } finally { Monitor.Exit(_hidReport); } }
/// <summary> /// Interprets a HID report sent by a DualShock 4 device. /// </summary> /// <param name="report">The HID report as byte array.</param> public override void ParseHidReport(byte[] report) { if (report[8] == 0xA3 && report[9] == 0x05) //feature report with crc { _calGetTries++; bool startDevice = false; //what we need to take crc32 of byte[] crcBuf = new byte[0x26]; Array.Copy(report, 8, crcBuf, 0, crcBuf.Length); //crc32 from the packet byte[] srcCrc32Bytes = new byte[4]; Array.Copy(report, 8 + crcBuf.Length, srcCrc32Bytes, 0, srcCrc32Bytes.Length); //crc32 computed uint crcInt = ScpControl.Shared.Utilities.Crc32.Compute(crcBuf); byte[] crcBytes = BitConverter.GetBytes(crcInt); if (crcBytes[0] == srcCrc32Bytes[0] && crcBytes[1] == srcCrc32Bytes[1] && crcBytes[2] == srcCrc32Bytes[2] && crcBytes[3] == srcCrc32Bytes[3]) { var calData = new byte[0x25]; Array.Copy(crcBuf, 1, calData, 0, calData.Length); string hex = BitConverter.ToString(calData).Replace('-', ' '); Log.InfoFormat("DS4 BTH CAL DATA({0}): {1}", calData.Length, hex); _cal = new DS4Cal(calData); startDevice = true; } else if (_calGetTries >= 3) { startDevice = true; Log.WarnFormat("DS4 Cal CRC32 failed after {0} tries, skipping", _calGetTries); _cal = null; } else if (startDevice == false) { //ask for it again m_Last = DateTime.Now; BluetoothDevice.HID_Command(HciHandle.Bytes, Get_SCID(L2CAP.PSM.HID_Command), _getCalFeatureReport); } if (startDevice) { State = DsState.Connected; _calGetTries = int.MinValue; m_Last = DateTime.Now; Rumble(0, 0); base.Start(); } return; } m_Packet++; var inputReport = NewHidReport(); Battery = (DsBattery)((byte)((report[41] + 2) / 2)); inputReport.PacketCounter = m_Packet; var buttons = (report[16] << 0) | (report[17] << 8) | (report[18] << 16); var trigger = false; //++ Convert HAT to DPAD report[16] &= 0xF0; switch ((uint)buttons & 0xF) { case 0: report[16] |= (byte)Ds4Button.Up.Offset; break; case 1: report[16] |= (byte)(Ds4Button.Up.Offset | Ds4Button.Right.Offset); break; case 2: report[16] |= (byte)Ds4Button.Right.Offset; break; case 3: report[16] |= (byte)(Ds4Button.Right.Offset | Ds4Button.Down.Offset); break; case 4: report[16] |= (byte)Ds4Button.Down.Offset; break; case 5: report[16] |= (byte)(Ds4Button.Down.Offset | Ds4Button.Left.Offset); break; case 6: report[16] |= (byte)Ds4Button.Left.Offset; break; case 7: report[16] |= (byte)(Ds4Button.Left.Offset | Ds4Button.Up.Offset); break; } //-- // apply calibration if we have it if (_cal != null) { _cal.ApplyCalToInReport(report, 11); } // copy controller data to report packet Buffer.BlockCopy(report, 11, inputReport.RawBytes, 8, 76); // convert wrapped time to absolute time var timestamp = (ushort)inputReport.Timestamp; if (_prevReportTimestamp < 0) //first one, start from zero { inputReport.Timestamp = ((uint)timestamp * 16) / 3; } else { ushort delta; if (_prevReportTimestamp > timestamp) //wrapped around { delta = (ushort)(ushort.MaxValue - _prevReportTimestamp + timestamp + 1); } else { delta = (ushort)(timestamp - _prevReportTimestamp); } inputReport.Timestamp = _prevFullTime + (((uint)delta * 16) / 3); } _prevReportTimestamp = timestamp; _prevFullTime = inputReport.Timestamp; // set report ID inputReport.RawBytes[8] = report[9]; // Quick Disconnect if (inputReport[Ds4Button.L1].IsPressed && inputReport[Ds4Button.R1].IsPressed && inputReport[Ds4Button.Ps].IsPressed) { trigger = true; // unset PS button inputReport.Unset(Ds4Button.Ps); } if (inputReport.IsPadActive) { m_IsIdle = false; } else if (!m_IsIdle) { m_IsIdle = true; m_Idle = DateTime.Now; } if (trigger && !m_IsDisconnect) { m_IsDisconnect = true; m_Disconnect = DateTime.Now; } else if (!trigger && m_IsDisconnect) { m_IsDisconnect = false; } OnHidReportReceived(inputReport); }
protected override void Process(DateTime now) { if (!Monitor.TryEnter(_hidOutputReport) || State != DsState.Connected) { return; } try { #region LED manipulation if ((now - m_Tick).TotalMilliseconds >= 500 && m_Packet > 0) { m_Tick = now; if (m_Queued == 0) { m_Queued = 1; } _ledStatus = 0; switch (GlobalConfiguration.Instance.Ds3LEDsFunc) { case 0: _ledStatus = 0; break; case 1: if (GlobalConfiguration.Instance.Ds3PadIDLEDsFlashCharging && Battery == DsBattery.Low) { _counterForLeds++; _counterForLeds %= 2; if (_counterForLeds == 1) { _ledStatus = _ledOffsets[(int)PadId]; } } else { _ledStatus = _ledOffsets[(int)PadId]; } break; case 2: switch (Battery) { case DsBattery.None: _ledStatus = (byte)(_ledOffsets[0] | _ledOffsets[3]); break; case DsBattery.Dying: _ledStatus = (byte)(_ledOffsets[1] | _ledOffsets[2]); break; case DsBattery.Low: _counterForLeds++; _counterForLeds %= 2; if (_counterForLeds == 1) { _ledStatus = _ledOffsets[0]; } break; case DsBattery.Medium: _ledStatus = (byte)(_ledOffsets[0] | _ledOffsets[1]); break; case DsBattery.High: _ledStatus = (byte)(_ledOffsets[0] | _ledOffsets[1] | _ledOffsets[2]); break; case DsBattery.Full: _ledStatus = (byte)(_ledOffsets[0] | _ledOffsets[1] | _ledOffsets[2] | _ledOffsets[3]); break; default: ; break; } break; case 3: if (GlobalConfiguration.Instance.Ds3LEDsCustom1) { _ledStatus |= _ledOffsets[0]; } if (GlobalConfiguration.Instance.Ds3LEDsCustom2) { _ledStatus |= _ledOffsets[1]; } if (GlobalConfiguration.Instance.Ds3LEDsCustom3) { _ledStatus |= _ledOffsets[2]; } if (GlobalConfiguration.Instance.Ds3LEDsCustom4) { _ledStatus |= _ledOffsets[3]; } break; default: _ledStatus = 0; break; } _hidOutputReport[11] = _ledStatus; } #endregion #region Fake DS3 workaround // TODO: this works for some but breaks others, so... dafuq >_< if (IsFake) { //_hidOutputReport[0] = 0xA2; //_hidOutputReport[3] = 0xFF; //_hidOutputReport[5] = 0x00; } #endregion if (m_Blocked || m_Queued <= 0) { return; } if (!((now - m_Last).TotalMilliseconds >= GlobalConfiguration.Instance.Latency)) { return; } m_Last = now; m_Blocked = true; m_Queued--; BluetoothDevice.HID_Command(HciHandle.Bytes, Get_SCID(L2CAP.PSM.HID_Command), _hidOutputReport); } finally { Monitor.Exit(_hidOutputReport); } }
protected override void Process(DateTime now) { if (!Monitor.TryEnter(_hidReport) || State != DsState.Connected) { return; } try { #region Light bar manipulation if (!GlobalConfiguration.Instance.IsLightBarDisabled) { if (Battery == DsBattery.Dying) { if (!_flash) { _hidReport[12] = _hidReport[13] = 0x40; _flash = true; m_Queued = 1; } } else { if (_flash) { _hidReport[12] = _hidReport[13] = 0x00; _flash = false; m_Queued = 1; } } } if (GlobalConfiguration.Instance.Brightness != _brightness) { _brightness = GlobalConfiguration.Instance.Brightness; } if (GlobalConfiguration.Instance.Ds4ShowBatteryInfo) { switch (Battery) { case DsBattery.Dying: SetLightBarColorUInt(GlobalConfiguration.Instance.Ds4ColorDying); break; case DsBattery.Low: SetLightBarColorUInt(GlobalConfiguration.Instance.Ds4ColorLow); break; case DsBattery.Medium: SetLightBarColorUInt(GlobalConfiguration.Instance.Ds4ColorMedium); break; case DsBattery.High: case DsBattery.Charging: SetLightBarColorUInt(GlobalConfiguration.Instance.Ds4ColorHigh); break; case DsBattery.Full: case DsBattery.Ultra: case DsBattery.Charged: SetLightBarColorUInt(GlobalConfiguration.Instance.Ds4ColorFull); break; default: SetLightBarColorUInt(GlobalConfiguration.Instance.Ds4ColorDying); break; } } else if (XInputSlot.HasValue) { SetLightBarColor((DsPadId)XInputSlot); } #endregion if ((now - m_Last).TotalMilliseconds >= 500) { if (_hidReport[7] > 0x00 || _hidReport[8] > 0x00) { m_Queued = 1; } } if (!m_Blocked && m_Queued > 0) { m_Last = now; m_Blocked = true; m_Queued--; BluetoothDevice.HID_Command(HciHandle.Bytes, Get_SCID(L2CAP.PSM.HID_Command), _hidReport); } } finally { Monitor.Exit(_hidReport); } }