void HCI_task()
        {
            switch (hci_state)
            {
                case HCI_INIT_STATE:
                    hci_counter++;
                    if (hci_counter > 10)
                    {
                        // wait until we have looped 10 times to clear any old events
                        WriteSerial("Init State");
                        hci_reset();
                        hci_state = HCI_RESET_STATE;
                        hci_counter = 0;
                    }
                    break;

                case HCI_RESET_STATE:
                    hci_counter++;
                    if (hciflag(hci_event.HCI_FLAG_CMD_COMPLETE))
                    {
                        WriteSerial("HCI Reset Complete");
                        hci_state = HCI_BDADDR_STATE;
                        hci_read_bdaddr();
                        hci_counter = 0;
                    }
                    if (hci_counter > 100)
                    {
                        WriteSerial("No Response to HCI Reset - Try reconnecting the Bluetooth Dongle");
                        hci_state = HCI_INIT_STATE;
                        hci_counter = 0;
                    }
                    break;

                case HCI_BDADDR_STATE:
                    if (hciflag(hci_event.HCI_FLAG_CMD_COMPLETE))
                    {
                        WriteSerial("Local Bluetooth Address: " + ByteToHex(my_bdaddr[5]).ToString() + ":" + ByteToHex(my_bdaddr[4]).ToString() + ":" + ByteToHex(my_bdaddr[3]).ToString() + ":" + ByteToHex(my_bdaddr[2]).ToString() + ":" + ByteToHex(my_bdaddr[1]).ToString() + ":" + ByteToHex(my_bdaddr[0]).ToString());
                        BDaddr[0] = my_bdaddr[5];//The commands are sent as LSB
                        BDaddr[1] = my_bdaddr[4];
                        BDaddr[2] = my_bdaddr[3];
                        BDaddr[3] = my_bdaddr[2];
                        BDaddr[4] = my_bdaddr[1];
                        BDaddr[5] = my_bdaddr[0];

                        hci_state = HCI_SCANNING_STATE;
                    }
                    break;

                case HCI_SCANNING_STATE:
                    WriteSerial("Wait For Incoming Connection Request");
                    hci_write_scan_enable();
                    hci_state = HCI_CONNECT_IN_STATE;
                    break;

                case HCI_CONNECT_IN_STATE:
                    if (hciflag(hci_event.HCI_FLAG_INCOMING_REQUEST))
                    {
                        WriteSerial("Incoming Request");
                        hci_remote_name(0);
                        hci_state = HCI_REMOTE_NAME;
                    }
                    break;

                case HCI_REMOTE_NAME:
                    if (hciflag(hci_event.HCI_FLAG_REMOTE_NAME))
                    {
                        WriteSerial("Remote Name:");
                        byte i;
                        for (i = 0; i < 40; i++)
                        {
                            if (remote_name[i] == 0x00)
                                break;
                        }
                        remote_name[i] = 13;// Carriage Return
                        remote_name[i + 1] = 10;// Line Feed

                        UART.Write(remote_name, 0, i + 2);
                        hci_accept_connection();
                        hci_state = HCI_CONNECTED_STATE;
                    }
                    break;

                case HCI_CONNECTED_STATE:
                    if (hciflag(hci_event.HCI_FLAG_CONN_COMPLETE))
                    {
                        WriteSerial("Connected to Device: " + ByteToHex(disc_bdaddr[5]) + ":" + ByteToHex(disc_bdaddr[4]) + ":" + ByteToHex(disc_bdaddr[3]) + ":" + ByteToHex(disc_bdaddr[2]) + ":" + ByteToHex(disc_bdaddr[1]) + ":" + ByteToHex(disc_bdaddr[0]));
                        hci_write_scan_disable();//Only allow one controller
                        hci_state = HCI_DISABLE_SCAN;
                    }
                    break;

                case HCI_DISABLE_SCAN:
                    if (hciflag(hci_event.HCI_FLAG_CMD_COMPLETE))
                    {
                        WriteSerial("Scan Disabled");
                        l2cap_state = L2CAP_EV_CONTROL_SETUP;
                        hci_state = HCI_DONE_STATE;
                    }
                    break;

                case HCI_DONE_STATE:
                    if (hciflag(hci_event.HCI_FLAG_DISCONN_COMPLETE))
                        hci_state = HCI_DISCONNECT_STATE;
                    break;

                case HCI_DISCONNECT_STATE:
                    if (hciflag(hci_event.HCI_FLAG_DISCONN_COMPLETE))
                    {
                        WriteSerial("Disconnected from Device: " + ByteToHex(disc_bdaddr[5]) + ":" + ByteToHex(disc_bdaddr[4]) + ":" + ByteToHex(disc_bdaddr[3]) + ":" + ByteToHex(disc_bdaddr[2]) + ":" + ByteToHex(disc_bdaddr[1]) + ":" + ByteToHex(disc_bdaddr[0]));
                        l2cap_event_status = 0;//Clear all flags
                        hci_event_flag = 0;//Clear all flags

                        //Reset all buffers
                        for (byte i = 0; i < IntInBuffer.Length; i++)
                            IntInBuffer[i] = 0;
                        for (byte i = 0; i < HCIBuffer.Length; i++)
                            HCIBuffer[i] = 0;
                        for (byte i = 0; i < BulkInBuffer.Length; i++)
                            BulkInBuffer[i] = 0;
                        for (byte i = 0; i < L2CAPBuffer.Length; i++)
                            L2CAPBuffer[i] = 0;
                        for (int i = 0; i < PS3Controller.OUTPUT_REPORT_BUFFER.Length; i++)//First two bytes reserved for report type and ID
                            HIDBuffer[i + 2] = PS3Controller.OUTPUT_REPORT_BUFFER[i];
                        for (int i = 2; i < HIDMoveBuffer.Length; i++)//First two bytes reserved for DATA request and ID
                            HIDMoveBuffer[i] = 0;

                        l2cap_state = L2CAP_EV_WAIT;
                        hci_state = HCI_SCANNING_STATE;
                    }
                    break;
                default:
                    break;

            }
        }
 void hci_remote_name(byte disc_device)
 {
     hci_event_flag &= ~(hci_event.HCI_FLAG_REMOTE_NAME);//Clear flag
     HCIBuffer[0] = 0x19; // HCI OCF = 19
     HCIBuffer[1] = 0x01 << 2; // HCI OGF = 1
     HCIBuffer[2] = 0x0A; // parameter length 10
     HCIBuffer[3] = disc_bdaddr[0]; // 6 octet bdaddr
     HCIBuffer[4] = disc_bdaddr[1];
     HCIBuffer[5] = disc_bdaddr[2];
     HCIBuffer[6] = disc_bdaddr[3];
     HCIBuffer[7] = disc_bdaddr[4];
     HCIBuffer[8] = disc_bdaddr[5];
     HCIBuffer[9] = 0x01;//Page Scan Repetition Mode
     HCIBuffer[10] = 0x00;//Reserved
     HCIBuffer[11] = 0x00;//Clock offset - low byte
     HCIBuffer[12] = 0x00;//Clock offset - high byte
     HCI_Command(HCIBuffer, 13);
 }
 void hci_reset()
 {
     hci_event_flag = 0; // clear all flags
     HCIBuffer[0] = 0x03;// HCI OCF = 3
     HCIBuffer[1] = 0x03 << 2; // HCI OGF = 3
     HCIBuffer[2] = 0x00; // parameter length = 0
     HCI_Command(HCIBuffer, 3);
 }
 void hci_disconnect()
 {
     hci_event_flag &= ~(hci_event.HCI_FLAG_DISCONN_COMPLETE);
     HCIBuffer[0] = 0x06; // HCI OCF = 6
     HCIBuffer[1] = 0x01 << 2; // HCI OGF = 1
     HCIBuffer[2] = 0x03; // parameter length = 3
     HCIBuffer[3] = (byte)(hci_handle & 0xFF);//connection handle - low byte
     HCIBuffer[4] = (byte)((hci_handle >> 8) & 0x0F);//connection handle - high byte
     HCIBuffer[5] = 0x13; // reason = Remote User Terminated Connection
     HCI_Command(HCIBuffer, 6);
 }
        /************************************************************/
        /*                    HCI Commands                        */
        /************************************************************/
        void HCI_Command(byte[] buf, int nbytes)
        {
            hci_command_packets--;
            hci_event_flag &= ~(hci_event.HCI_FLAG_CMD_COMPLETE);

            try
            {
                //Single FunctionPrimary Controller - Standard request
                raw.SendSetupTransfer(0x20, 0x00, 0x0000, 0x0000, buf, 0, nbytes);
            }
            catch
            {
                WriteSerial("HCI_Command failed");
            }
        }
 void hci_accept_connection()
 {
     hci_event_flag &= ~(hci_event.HCI_FLAG_INCOMING_REQUEST);//Clear flag
     HCIBuffer[0] = 0x09; // HCI OCF = 9
     HCIBuffer[1] = 0x01 << 2; // HCI OGF = 1
     HCIBuffer[2] = 0x07; // parameter length 7
     HCIBuffer[3] = disc_bdaddr[0]; // 6 octet bdaddr
     HCIBuffer[4] = disc_bdaddr[1];
     HCIBuffer[5] = disc_bdaddr[2];
     HCIBuffer[6] = disc_bdaddr[3];
     HCIBuffer[7] = disc_bdaddr[4];
     HCIBuffer[8] = disc_bdaddr[5];
     HCIBuffer[9] = 0x00; //switch role to master
     HCI_Command(HCIBuffer, 10);
     return;
 }
 private bool hciflag(hci_event flag)
 {
     if ((hci_event_flag & flag) == flag)
         return true;
     else
         return false;
 }
        private void IntReadingThread()
        {
            while (true)
            {
                //Read every bInterval
                Thread.Sleep(IntInPipe.PipeEndpoint.bInterval);
                {
                    try
                    {
                        IntInPipe.TransferData(IntInBuffer, 0, IntInBuffer.Length);
                        // uncomment for debugging
                        /*
                            int i = 0;
                            WriteSerial(
                                ByteToHex(IntInBuffer[i++]) + " " + ByteToHex(IntInBuffer[i++]) + " " + ByteToHex(IntInBuffer[i++]) + " " + ByteToHex(IntInBuffer[i++]) + " " +
                                ByteToHex(IntInBuffer[i++]) + " " + ByteToHex(IntInBuffer[i++]) + " " + ByteToHex(IntInBuffer[i++]) + " " + ByteToHex(IntInBuffer[i++]) + " " +
                                ByteToHex(IntInBuffer[i++]) + " " + ByteToHex(IntInBuffer[i++]) + " " + ByteToHex(IntInBuffer[i++]) + " " + ByteToHex(IntInBuffer[i++]) + " " +
                                ByteToHex(IntInBuffer[i++]) + " " + ByteToHex(IntInBuffer[i++]) + " " + ByteToHex(IntInBuffer[i++]) + " " + ByteToHex(IntInBuffer[i++])
                                );
                        */
                        if (remote_name_bool)
                        {
                            for (byte i2 = 0; i2 < IntInBuffer.Length; i2++)
                            {
                                if (IntInBuffer[i2] == 0x00)
                                {
                                    Debug.Print("Finished Reading Name");
                                    remote_name_bool = false;
                                    hci_event_flag |= hci_event.HCI_FLAG_REMOTE_NAME;
                                    break;
                                }
                                if (IntInBuffer[0] == 0x54 || IntInBuffer[0] == 0x43 || IntInBuffer[0] == 0x69)// Second Line - 'T' in "PLAYSTATION(R)3 Controller" or 'C' in "Motion Controller" or 'i' in "Navigation Controller"
                                    remote_name[7 + i2] = IntInBuffer[i2];
                                else if (IntInBuffer[0] == 0x6C)// Third Line - "l" in "PLAYSTATION(R)3 Controller"
                                    remote_name[23 + i2] = IntInBuffer[i2];
                                else
                                    break;
                            }
                        }
                        switch (IntInBuffer[0])
                        {
                            /*  buf[0] = Event Code                            */
                            /*  buf[1] = Parameter Total Length                */
                            /*  buf[n] = Event Parameters based on each event  */

                            case EV_REMOTE_NAME_COMPLETE:
                                for (byte n = 0; n < remote_name.Length; n++)//Reset remote name
                                    remote_name[n] = 0;

                                for (byte n = 9; n < IntInBuffer.Length; n++)
                                    remote_name[n - 9] = IntInBuffer[n];
                                remote_name_bool = true;
                                break;

                            case EV_COMMAND_COMPLETE:
                                //Debug.Print("Command Complete");
                                hci_command_packets = IntInBuffer[2];//Update flow control
                                if (IntInBuffer[5] != 0x00)// Check if success
                                {
                                    hci_event_flag = ~hci_event.HCI_FLAG_CMD_COMPLETE;//Set flag
                                    WriteSerial("HCI Command Complete Failed - OGF: 0x" + (IntInBuffer[4] >> 2) + " OCF: 0x" + IntInBuffer[3]);
                                    return;
                                }

                                hci_event_flag |= hci_event.HCI_FLAG_CMD_COMPLETE;//Set flag

                                // parameters from read local bluetooth address
                                if ((IntInBuffer[3] == 0x09) && (IntInBuffer[4] == 0x10))
                                {
                                    for (byte n = 0; n < 6; n++)
                                        my_bdaddr[n] = IntInBuffer[6 + n];
                                    //Debug.Print("Read Local Bluetooth Address: " + ByteToHex(my_bdaddr[5]).ToString() + ":" + ByteToHex(my_bdaddr[4]).ToString() + ":" + ByteToHex(my_bdaddr[3]).ToString() + ":" + ByteToHex(my_bdaddr[2]).ToString() + ":" + ByteToHex(my_bdaddr[1]).ToString() + ":" + ByteToHex(my_bdaddr[0]).ToString());
                                }
                                break;

                            case EV_COMMAND_STATUS:
                                //Debug.Print("Command Status");
                                hci_command_packets = IntInBuffer[3];// update flow control
                                hci_event_flag |= hci_event.HCI_FLAG_CMD_STATUS;

                                if (IntInBuffer[2] == 0)
                                    Debug.Print("Command Status - Complete: 0x" + ByteToHex(IntInBuffer[5]) + ByteToHex(IntInBuffer[4]));
                                else// show status on serial if not OK
                                    WriteSerial("HCI Command Failed - Error: 0x" + ByteToHex(IntInBuffer[2]) + " Command: 0x" + ByteToHex(IntInBuffer[5]) + ByteToHex(IntInBuffer[4]));
                                break;

                            case EV_CONNECT_COMPLETE:
                                if (IntInBuffer[2] == 0)// check if connected OK
                                {
                                    hci_handle = IntInBuffer[3] | IntInBuffer[4] << 8; //store the handle for the ACL connection
                                    Debug.Print("Connect Complete - HCI Handle: " + hci_handle);
                                    hci_event_flag |= hci_event.HCI_FLAG_CONN_COMPLETE; //set connection OK flag
                                }
                                break;

                            case EV_DISCONNECT_COMPLETE:
                                if (IntInBuffer[2] == 0)// check if disconnected OK
                                {
                                    string reason = "Unknown Reason";
                                    if (IntInBuffer[5] == 0x08)
                                        reason = "Connection Timeout";
                                    else if (IntInBuffer[5] == 0x13)
                                        reason = "Remote User Terminated Connection";
                                    else if (IntInBuffer[5] == 0x14)
                                        reason = "Remote Device Terminated Connection due to Low Resources";
                                    else if (IntInBuffer[5] == 0x15)
                                        reason = "Remote Device Terminated Connection due to Power Off";
                                    else if (IntInBuffer[5] == 0x16)
                                        reason = "Connection Terminated By Local Host";

                                    Debug.Print("Disconnect Complete - Reason: " + reason);

                                    hci_event_flag |= hci_event.HCI_FLAG_DISCONN_COMPLETE;
                                    hci_event_flag &= ~(hci_event.HCI_FLAG_CONN_COMPLETE); //clear connection OK flag
                                }
                                break;

                            case EV_NUM_COMPLETE_PKT:
                                //Debug.Print("Number Of Completed Packets: " + (uint)(IntInBuffer[6] | IntInBuffer[7] << 8));
                                break;

                            case EV_INCOMING_CONNECT:
                                //Infact it also sends the class and link type, but it only stores the bluetooth address
                                disc_bdaddr[0] = IntInBuffer[2];
                                disc_bdaddr[1] = IntInBuffer[3];
                                disc_bdaddr[2] = IntInBuffer[4];
                                disc_bdaddr[3] = IntInBuffer[5];
                                disc_bdaddr[4] = IntInBuffer[6];
                                disc_bdaddr[5] = IntInBuffer[7];

                                Debug.Print("Incoming Connection");
                                Debug.Print("Bluetooth Address: " + ByteToHex(disc_bdaddr[5]) + ":" + ByteToHex(disc_bdaddr[4]) + ":" + ByteToHex(disc_bdaddr[3]) + ":" + ByteToHex(disc_bdaddr[2]) + ":" + ByteToHex(disc_bdaddr[1]) + ":" + ByteToHex(disc_bdaddr[0]));
                                hci_event_flag |= hci_event.HCI_FLAG_INCOMING_REQUEST;
                                break;

                            case EV_ROLE_CHANGED:
                                Debug.Print("Role Changed");
                                dev_role = IntInBuffer[9];
                                break;

                            default:
                                if (IntInBuffer[0] != 0x00)
                                    Debug.Print("Unmanaged event: " + ByteToHex(IntInBuffer[0]));
                                break;
                        }
                        HCI_task();//Start HCI_task
                    }
                    catch (Exception ex)
                    {
                        WriteSerial("Exception was thrown - try reconnecting the bluetooth dongle");
                        Debug.Print("==============================");
                        Debug.Print(DateTime.Now.ToString());
                        Debug.Print(ex.Message);
                        Debug.Print(ex.StackTrace);
                        if (ex.InnerException != null)
                        {
                            Debug.Print("Inner Exception: " + ex.InnerException.Message);
                        }
                    }
                }
            }
        }