예제 #1
0
        /// <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);
        }
예제 #2
0
        /// <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);
        }
예제 #3
0
        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());
        }
예제 #4
0
        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);
        }
예제 #5
0
        /// <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);
        }
예제 #6
0
        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);
        }
예제 #7
0
        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());
        }
예제 #8
0
        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);
            }
        }
예제 #9
0
        /// <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);
        }
예제 #10
0
        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);
            }
        }
예제 #11
0
        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);
            }
        }