diff --git a/BTD.cpp b/BTD.cpp new file mode 100644 index 00000000..6d102141 --- /dev/null +++ b/BTD.cpp @@ -0,0 +1,970 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#include "BTD.h" +#define DEBUG // Uncomment to print data for debugging +//#define EXTRADEBUG // Uncomment to get even more debugging data + +const uint8_t BTD::BTD_EVENT_PIPE = 1; +const uint8_t BTD::BTD_DATAIN_PIPE = 2; +const uint8_t BTD::BTD_DATAOUT_PIPE = 3; + +BTD::BTD(USB *p): +pUsb(p), // Pointer to USB class instance - mandatory +bAddress(0), // Device address - mandatory +bNumEP(1), // If config descriptor needs to be parsed +qNextPollTime(0), // Reset NextPollTime +bPollEnable(false) // Don't start polling before dongle is connected +{ + for(uint8_t i=0; iRegisterDeviceClass(this); //set devConfig[] entry +} + +uint8_t BTD::Init(uint8_t parent, uint8_t port, bool lowspeed) { + uint8_t buf[sizeof(USB_DEVICE_DESCRIPTOR)]; + uint8_t rcode; + UsbDevice *p = NULL; + EpInfo *oldep_ptr = NULL; + uint8_t num_of_conf; // number of configurations + uint16_t PID; + uint16_t VID; + + // get memory address of USB device address pool + AddressPool &addrPool = pUsb->GetAddressPool(); +#ifdef EXTRADEBUG + Notify(PSTR("\r\nBTD Init")); +#endif + // check if address has already been assigned to an instance + if (bAddress) { +#ifdef DEBUG + Notify(PSTR("\r\nAddress in use")); +#endif + return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; + } + + // Get pointer to pseudo device with address 0 assigned + p = addrPool.GetUsbDevicePtr(0); + + if (!p) { +#ifdef DEBUG + Notify(PSTR("\r\nAddress not found")); +#endif + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + } + + if (!p->epinfo) { +#ifdef DEBUG + Notify(PSTR("\r\nepinfo is null")); +#endif + return USB_ERROR_EPINFO_IS_NULL; + } + + // Save old pointer to EP_RECORD of address 0 + oldep_ptr = p->epinfo; + + // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence + p->epinfo = epInfo; + + p->lowspeed = lowspeed; + + // Get device descriptor + rcode = pUsb->getDevDescr(0, 0, sizeof(USB_DEVICE_DESCRIPTOR), (uint8_t*)buf);// Get device descriptor - addr, ep, nbytes, data + + // Restore p->epinfo + p->epinfo = oldep_ptr; + + if(rcode) + goto FailGetDevDescr; + + // Allocate new address according to device class + bAddress = addrPool.AllocAddress(parent, false, port); + + if (!bAddress) + return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; + + // Extract Max Packet Size from device descriptor + epInfo[0].maxPktSize = (uint8_t)((USB_DEVICE_DESCRIPTOR*)buf)->bMaxPacketSize0; + + // Assign new address to the device + rcode = pUsb->setAddr( 0, 0, bAddress ); + if (rcode) + { + p->lowspeed = false; + addrPool.FreeAddress(bAddress); + bAddress = 0; +#ifdef DEBUG + Notify(PSTR("\r\nsetAddr: ")); +#endif + PrintHex(rcode); + return rcode; + } +#ifdef EXTRADEBUG + Notify(PSTR("\r\nAddr: ")); + PrintHex(bAddress); +#endif + p->lowspeed = false; + + //get pointer to assigned address record + p = addrPool.GetUsbDevicePtr(bAddress); + if (!p) + return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; + + p->lowspeed = lowspeed; + + // Assign epInfo to epinfo pointer - only EP0 is known + rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); + if (rcode) + goto FailSetDevTblEntry; + VID = ((USB_DEVICE_DESCRIPTOR*)buf)->idVendor; + PID = ((USB_DEVICE_DESCRIPTOR*)buf)->idProduct; + + if(VID == PS3_VID && (PID == PS3_PID || PID == PS3NAVIGATION_PID || PID == PS3MOVE_PID)) { + /* The application will work in reduced host mode, so we can save program and data + memory space. After verifying the PID and VID we will use known values for the + configuration values for device, interface, endpoints and HID for the PS3 Controllers */ + + /* Initialize data structures for endpoints of device */ + epInfo[ PS3_OUTPUT_PIPE ].epAddr = 0x02; // PS3 output endpoint + epInfo[ PS3_OUTPUT_PIPE ].epAttribs = EP_INTERRUPT; + epInfo[ PS3_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints + epInfo[ PS3_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE; + epInfo[ PS3_OUTPUT_PIPE ].bmSndToggle = bmSNDTOG0; + epInfo[ PS3_OUTPUT_PIPE ].bmRcvToggle = bmRCVTOG0; + epInfo[ PS3_INPUT_PIPE ].epAddr = 0x01; // PS3 report endpoint + epInfo[ PS3_INPUT_PIPE ].epAttribs = EP_INTERRUPT; + epInfo[ PS3_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints + epInfo[ PS3_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE; + epInfo[ PS3_INPUT_PIPE ].bmSndToggle = bmSNDTOG0; + epInfo[ PS3_INPUT_PIPE ].bmRcvToggle = bmRCVTOG0; + + rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo); + if( rcode ) + goto FailSetDevTblEntry; + + delay(200);//Give time for address change + + rcode = pUsb->setConf(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, 1); + if( rcode ) + goto FailSetConf; + + if(PID == PS3_PID || PID == PS3NAVIGATION_PID) { + if(PID == PS3_PID) { +#ifdef DEBUG + Notify(PSTR("\r\nDualshock 3 Controller Connected")); +#endif + } else { // must be a navigation controller +#ifdef DEBUG + Notify(PSTR("\r\nNavigation Controller Connected")); +#endif + } + /* Set internal bluetooth address */ + setBdaddr(my_bdaddr); + } + else { // must be a Motion controller +#ifdef DEBUG + Notify(PSTR("\r\nMotion Controller Connected")); +#endif + setMoveBdaddr(my_bdaddr); + } + } + else { + num_of_conf = ((USB_DEVICE_DESCRIPTOR*)buf)->bNumConfigurations; + + // check if attached device is a Bluetooth dongle and fill endpoint data structure + // first interface in the configuration must have Bluetooth assigned Class/Subclass/Protocol + // and 3 endpoints - interrupt-IN, bulk-IN, bulk-OUT, + // not necessarily in this order + for (uint8_t i=0; i confDescrParser(this); + rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser); + if(rcode) + goto FailGetConfDescr; + if(bNumEP >= BTD_MAX_ENDPOINTS) // All endpoints extracted + break; + } + + if (bNumEP < BTD_MAX_ENDPOINTS) + goto FailUnknownDevice; + + // Assign epInfo to epinfo pointer - this time all 3 endpoins + rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo); + if(rcode) + goto FailSetDevTblEntry; + + delay(200); // Give time for address change + + // Set Configuration Value + rcode = pUsb->setConf(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, bConfNum); + if(rcode) + goto FailSetConf; + + hci_num_reset_loops = 100; // only loop 100 times before trying to send the hci reset command + hci_counter = 0; + hci_state = HCI_INIT_STATE; + watingForConnection = false; + bPollEnable = true; + +#ifdef DEBUG + Notify(PSTR("\r\nBluetooth Dongle Initialized")); +#endif + } + return 0; // Successful configuration + + /* diagnostic messages */ +FailGetDevDescr: +#ifdef DEBUG + Notify(PSTR("\r\ngetDevDescr")); +#endif + goto Fail; +FailSetDevTblEntry: +#ifdef DEBUG + Notify(PSTR("\r\nsetDevTblEn")); +#endif + goto Fail; +FailGetConfDescr: +#ifdef DEBUG + Notify(PSTR("\r\ngetConf")); +#endif + goto Fail; +FailSetConf: +#ifdef DEBUG + Notify(PSTR("\r\nsetConf")); +#endif + goto Fail; +FailUnknownDevice: +#ifdef DEBUG + Notify(PSTR("\r\nUnknown Device Connected - VID: ")); + PrintHex(VID); + Notify(PSTR(" PID: ")); + PrintHex(PID); +#endif + rcode = -1; + goto Fail; +Fail: +#ifdef DEBUG + Notify(PSTR("\r\nBTD Init Failed, error code: ")); + Serial.print(rcode); +#endif + Release(); + return rcode; +} +/* Extracts interrupt-IN, bulk-IN, bulk-OUT endpoint information from config descriptor */ +void BTD::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) { + //ErrorMessage(PSTR("Conf.Val"),conf); + //ErrorMessage(PSTR("Iface Num"),iface); + //ErrorMessage(PSTR("Alt.Set"),alt); + + if(alt) // wrong interface - by BT spec, no alt setting + return; + + bConfNum = conf; + uint8_t index; + + if ((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80) // Interrupt In endpoint found + index = BTD_EVENT_PIPE; + + else { + if ((pep->bmAttributes & 0x02) == 2) // bulk endpoint found + index = ((pep->bEndpointAddress & 0x80) == 0x80) ? BTD_DATAIN_PIPE : BTD_DATAOUT_PIPE; + else + return; + } + + // Fill the rest of endpoint data structure + epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F); + epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize; +#ifdef EXTRADEBUG + PrintEndpointDescriptor(pep); +#endif + if(pollInterval < pep->bInterval) // Set the polling interval as the largest polling interval obtained from endpoints + pollInterval = pep->bInterval; + bNumEP++; + return; +} +void BTD::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) { + Notify(PSTR("\r\nEndpoint descriptor:")); + Notify(PSTR("\r\nLength:\t\t")); + PrintHex(ep_ptr->bLength); + Notify(PSTR("\r\nType:\t\t")); + PrintHex(ep_ptr->bDescriptorType); + Notify(PSTR("\r\nAddress:\t")); + PrintHex(ep_ptr->bEndpointAddress); + Notify(PSTR("\r\nAttributes:\t")); + PrintHex(ep_ptr->bmAttributes); + Notify(PSTR("\r\nMaxPktSize:\t")); + PrintHex(ep_ptr->wMaxPacketSize); + Notify(PSTR("\r\nPoll Intrv:\t")); + PrintHex(ep_ptr->bInterval); +} + +/* Performs a cleanup after failed Init() attempt */ +uint8_t BTD::Release() { + for (uint8_t i=0; iRelease(); // Reset both the L2CAP Channel and the HCI Connection + pUsb->GetAddressPool().FreeAddress(bAddress); + bAddress = 0; + bPollEnable = false; + bNumEP = 1; // must have to be reset to 1 + return 0; +} +uint8_t BTD::Poll() { + if (!bPollEnable) + return 0; + if (qNextPollTime <= millis()) { // Don't poll if shorter than polling interval + qNextPollTime = millis() + pollInterval; // Set new poll time + HCI_event_task(); // poll the HCI event pipe + ACL_event_task(); // start polling the ACL input pipe too, though discard data until connected + } + return 0; +} + +void BTD::HCI_event_task() { + /* check the event pipe*/ + uint16_t MAX_BUFFER_SIZE = BULK_MAXPKTSIZE; // Request more than 16 bytes anyway, the inTransfer routine will take care of this + uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ BTD_EVENT_PIPE ].epAddr, &MAX_BUFFER_SIZE, hcibuf); // input on endpoint 1 + if(!rcode || rcode == hrNAK) // Check for errors + { + switch (hcibuf[0]) //switch on event type + { + case EV_COMMAND_COMPLETE: + hci_event_flag |= HCI_FLAG_CMD_COMPLETE; // set command complete flag + if (!hcibuf[5]) { // Check if command succeeded + if((hcibuf[3] == 0x01) && (hcibuf[4] == 0x10)) { // parameters from read local version information + hci_version = hcibuf[6]; // Used to check if it supports 2.0+EDR - see http://www.bluetooth.org/Technical/AssignedNumbers/hci.htm + hci_event_flag |= HCI_FLAG_READ_VERSION; + } else if((hcibuf[3] == 0x09) && (hcibuf[4] == 0x10)) { // parameters from read local bluetooth address + for (uint8_t i = 0; i < 6; i++) + my_bdaddr[i] = hcibuf[6 + i]; + hci_event_flag |= HCI_FLAG_READ_BDADDR; + } + } + break; + + case EV_COMMAND_STATUS: + if(hcibuf[2]) { // show status on serial if not OK +#ifdef DEBUG + Notify(PSTR("\r\nHCI Command Failed: ")); + PrintHex(hcibuf[2]); + Notify(PSTR(" ")); + PrintHex(hcibuf[4]); + Notify(PSTR(" ")); + PrintHex(hcibuf[5]); +#endif + } + break; + + case EV_CONNECT_COMPLETE: + if (!hcibuf[2]) { // check if connected OK + hci_handle = hcibuf[3] | hcibuf[4] << 8; //store the handle for the ACL connection + hci_event_flag |= HCI_FLAG_CONN_COMPLETE; // set connection complete flag + } + break; + + case EV_DISCONNECT_COMPLETE: + if (!hcibuf[2]) { // check if disconnected OK + hci_event_flag |= HCI_FLAG_DISCONN_COMPLETE; //set disconnect commend complete flag + hci_event_flag &= ~HCI_FLAG_CONN_COMPLETE; // clear connection complete flag + } + break; + + case EV_REMOTE_NAME_COMPLETE: + if (!hcibuf[2]) { // check if reading is OK + for (uint8_t i = 0; i < 30; i++) + remote_name[i] = hcibuf[9 + i]; //store first 30 bytes + hci_event_flag |= HCI_FLAG_REMOTE_NAME_COMPLETE; + } + break; + + case EV_INCOMING_CONNECT: + disc_bdaddr[0] = hcibuf[2]; + disc_bdaddr[1] = hcibuf[3]; + disc_bdaddr[2] = hcibuf[4]; + disc_bdaddr[3] = hcibuf[5]; + disc_bdaddr[4] = hcibuf[6]; + disc_bdaddr[5] = hcibuf[7]; + hci_event_flag |= HCI_FLAG_INCOMING_REQUEST; + break; + + case EV_PIN_CODE_REQUEST: + if(btdPin != NULL) { +#ifdef DEBUG + Notify(PSTR("\r\nBluetooth pin is set too: ")); + Serial.print(btdPin); +#endif + hci_pin_code_request_reply(btdPin); + } + else + hci_pin_code_negative_request_reply(); + break; + + case EV_LINK_KEY_REQUEST: +#ifdef DEBUG + Notify(PSTR("\r\nReceived Key Request")); +#endif + hci_link_key_request_negative_reply(); + break; + + /* We will just ignore the following events */ + case EV_NUM_COMPLETE_PKT: + case EV_ROLE_CHANGED: + case EV_PAGE_SCAN_REP_MODE: + case EV_LOOPBACK_COMMAND: + case EV_DATA_BUFFER_OVERFLOW: + case EV_CHANGE_CONNECTION_LINK: + case EV_AUTHENTICATION_COMPLETE: + case EV_MAX_SLOTS_CHANGE: + case EV_QOS_SETUP_COMPLETE: + case EV_LINK_KEY_NOTIFICATION: + case EV_ENCRYPTION_CHANGE: + case EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE: + break; + + default: +#ifdef EXTRADEBUG + if(hcibuf[0] != 0x00) { + Notify(PSTR("\r\nUnmanaged HCI Event: ")); + PrintHex(hcibuf[0]); + } +#endif + break; + } // switch + HCI_task(); + } + else { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nHCI event error: ")); + PrintHex(rcode); +#endif + } +} + +/* Poll Bluetooth and print result */ +void BTD::HCI_task() { + switch (hci_state){ + case HCI_INIT_STATE: + hci_counter++; + if (hci_counter > hci_num_reset_loops) { // wait until we have looped x times to clear any old events + hci_reset(); + hci_state = HCI_RESET_STATE; + hci_counter = 0; + } + break; + + case HCI_RESET_STATE: + hci_counter++; + if (hci_cmd_complete) { +#ifdef DEBUG + Notify(PSTR("\r\nHCI Reset complete")); +#endif + hci_state = HCI_BDADDR_STATE; + hci_read_bdaddr(); + } + else if (hci_counter > hci_num_reset_loops) { + hci_num_reset_loops *= 10; + if(hci_num_reset_loops > 2000) + hci_num_reset_loops = 2000; +#ifdef DEBUG + Notify(PSTR("\r\nNo response to HCI Reset")); +#endif + hci_state = HCI_INIT_STATE; + hci_counter = 0; + } + break; + + case HCI_BDADDR_STATE: + if (hci_read_bdaddr_complete) { +#ifdef DEBUG + Notify(PSTR("\r\nLocal Bluetooth Address: ")); + for(int8_t i = 5; i > 0;i--) { + PrintHex(my_bdaddr[i]); + Serial.print(":"); + } + PrintHex(my_bdaddr[0]); +#endif + hci_read_local_version_information(); + hci_state = HCI_LOCAL_VERSION_STATE; + } + break; + + case HCI_LOCAL_VERSION_STATE: + if (hci_read_version_complete) { + if(btdName != NULL) { + hci_set_local_name(btdName); + hci_state = HCI_SET_NAME_STATE; + } else + hci_state = HCI_SCANNING_STATE; + } + break; + + case HCI_SET_NAME_STATE: + if (hci_cmd_complete) { +#ifdef DEBUG + Notify(PSTR("\r\nThe name is set to: ")); + Serial.print(btdName); +#endif + hci_state = HCI_SCANNING_STATE; + } + break; + + case HCI_SCANNING_STATE: +#ifdef DEBUG + Notify(PSTR("\r\nWait For Incoming Connection Request")); +#endif + hci_write_scan_enable(); + watingForConnection = true; + hci_state = HCI_CONNECT_IN_STATE; + break; + + case HCI_CONNECT_IN_STATE: + if(hci_incoming_connect_request) { + watingForConnection = false; +#ifdef DEBUG + Notify(PSTR("\r\nIncoming Request")); +#endif + hci_remote_name(); + hci_state = HCI_REMOTE_NAME_STATE; + } else if (hci_disconnect_complete) + hci_state = HCI_DISCONNECT_STATE; + break; + + case HCI_REMOTE_NAME_STATE: + if(hci_remote_name_complete) { +#ifdef DEBUG + Notify(PSTR("\r\nRemote Name: ")); + for (uint8_t i = 0; i < 30; i++) + { + if(remote_name[i] == NULL) + break; + Serial.write(remote_name[i]); + } +#endif + hci_accept_connection(); + hci_state = HCI_CONNECTED_STATE; + } + break; + + case HCI_CONNECTED_STATE: + if (hci_connect_complete) { +#ifdef DEBUG + Notify(PSTR("\r\nConnected to Device: ")); + for(int8_t i = 5; i>0;i--) + { + PrintHex(disc_bdaddr[i]); + Serial.print(":"); + } + PrintHex(disc_bdaddr[0]); +#endif + hci_write_scan_disable(); + hci_state = HCI_DISABLE_SCAN_STATE; + } + break; + + case HCI_DISABLE_SCAN_STATE: + if (hci_cmd_complete) { +#ifdef DEBUG + Notify(PSTR("\r\nScan Disabled")); +#endif + hci_event_flag = 0; + hci_state = HCI_DONE_STATE; + } + break; + + case HCI_DONE_STATE: + if(connectionClaimed) { // Wait until one of the services has claimed the connection before accepting more incoming requests + hci_state = HCI_SCANNING_STATE; + connectionClaimed = false; + } + break; + + case HCI_DISCONNECT_STATE: + if (hci_disconnect_complete) { +#ifdef DEBUG + Notify(PSTR("\r\nHCI Disconnected from Device")); +#endif + hci_event_flag = 0; // Clear all flags + + // Reset all buffers + for (uint8_t i = 0; i < BULK_MAXPKTSIZE; i++) + hcibuf[i] = 0; + for (uint8_t i = 0; i < BULK_MAXPKTSIZE; i++) + l2capinbuf[i] = 0; + + hci_state = HCI_SCANNING_STATE; + } + break; + default: + break; + } +} + +void BTD::ACL_event_task() { + uint16_t MAX_BUFFER_SIZE = BULK_MAXPKTSIZE; + uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ BTD_DATAIN_PIPE ].epAddr, &MAX_BUFFER_SIZE, l2capinbuf); // input on endpoint 2 + if(!rcode) { // Check for errors + for (uint8_t i=0; iACLData(l2capinbuf); + } else if (rcode != hrNAK) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nACL data in error: ")); + PrintHex(rcode); +#endif + } + for (uint8_t i=0; iPoll(); +} + +/************************************************************/ +/* HCI Commands */ +/************************************************************/ +void BTD::HCI_Command(uint8_t* data, uint16_t nbytes) { + hci_event_flag &= ~HCI_FLAG_CMD_COMPLETE; + pUsb->ctrlReq(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, bmREQ_HCI_OUT, 0x00, 0x00, 0x00 ,0x00, nbytes, nbytes, data, NULL); +} +void BTD::hci_reset() { + hci_event_flag = 0; // Clear all the flags + hcibuf[0] = 0x03; // HCI OCF = 3 + hcibuf[1] = 0x03 << 2; // HCI OGF = 3 + hcibuf[2] = 0x00; + HCI_Command(hcibuf, 3); +} +void BTD::hci_write_scan_enable() { + hci_event_flag &= ~HCI_FLAG_INCOMING_REQUEST; + hcibuf[0] = 0x1A; // HCI OCF = 1A + hcibuf[1] = 0x03 << 2; // HCI OGF = 3 + hcibuf[2] = 0x01; // parameter length = 1 + if(btdName != NULL) + hcibuf[3] = 0x03; // Inquiry Scan enabled. Page Scan enabled. + else + hcibuf[3] = 0x02; // Inquiry Scan disabled. Page Scan enabled. + HCI_Command(hcibuf, 4); +} +void BTD::hci_write_scan_disable() { + hcibuf[0] = 0x1A; // HCI OCF = 1A + hcibuf[1] = 0x03 << 2; // HCI OGF = 3 + hcibuf[2] = 0x01; // parameter length = 1 + hcibuf[3] = 0x00; // Inquiry Scan disabled. Page Scan disabled. + HCI_Command(hcibuf, 4); +} +void BTD::hci_read_bdaddr() { + hcibuf[0] = 0x09; // HCI OCF = 9 + hcibuf[1] = 0x04 << 2; // HCI OGF = 4 + hcibuf[2] = 0x00; + HCI_Command(hcibuf, 3); +} +void BTD::hci_read_local_version_information() { + hcibuf[0] = 0x01; // HCI OCF = 1 + hcibuf[1] = 0x04 << 2; // HCI OGF = 4 + hcibuf[2] = 0x00; + HCI_Command(hcibuf, 3); +} +void BTD::hci_accept_connection() { + hcibuf[0] = 0x09; // HCI OCF = 9 + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x07; // parameter length 7 + hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr + hcibuf[4] = disc_bdaddr[1]; + hcibuf[5] = disc_bdaddr[2]; + hcibuf[6] = disc_bdaddr[3]; + hcibuf[7] = disc_bdaddr[4]; + hcibuf[8] = disc_bdaddr[5]; + hcibuf[9] = 0x00; //switch role to master + + HCI_Command(hcibuf, 10); +} +void BTD::hci_remote_name() { + hci_event_flag &= ~HCI_FLAG_REMOTE_NAME_COMPLETE; + hcibuf[0] = 0x19; // HCI OCF = 19 + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x0A; // parameter length = 10 + hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr + hcibuf[4] = disc_bdaddr[1]; + hcibuf[5] = disc_bdaddr[2]; + hcibuf[6] = disc_bdaddr[3]; + hcibuf[7] = disc_bdaddr[4]; + hcibuf[8] = disc_bdaddr[5]; + hcibuf[9] = 0x01; //Page Scan Repetition Mode + hcibuf[10] = 0x00; //Reserved + hcibuf[11] = 0x00; //Clock offset - low byte + hcibuf[12] = 0x00; //Clock offset - high byte + + HCI_Command(hcibuf, 13); +} +void BTD::hci_set_local_name(const char* name) { + hcibuf[0] = 0x13; // HCI OCF = 13 + hcibuf[1] = 0x03 << 2; // HCI OGF = 3 + hcibuf[2] = strlen(name)+1; // parameter length = the length of the string + end byte + uint8_t i; + for(i = 0; i < strlen(name); i++) + hcibuf[i+3] = name[i]; + hcibuf[i+3] = 0x00; // End of string + + HCI_Command(hcibuf, 4+strlen(name)); +} +void BTD::hci_pin_code_request_reply(const char* key) { + hcibuf[0] = 0x0D; // HCI OCF = 0D + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x17; // parameter length 23 + hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr + hcibuf[4] = disc_bdaddr[1]; + hcibuf[5] = disc_bdaddr[2]; + hcibuf[6] = disc_bdaddr[3]; + hcibuf[7] = disc_bdaddr[4]; + hcibuf[8] = disc_bdaddr[5]; + hcibuf[9] = strlen(key); // Length of key + uint8_t i; + for(i = 0; i < strlen(key); i++) // The maximum size of the key is 16 + hcibuf[i+10] = key[i]; + for(;i < 16; i++) + hcibuf[i+10] = 0x00; // The rest should be 0 + + HCI_Command(hcibuf, 26); +} +void BTD::hci_pin_code_negative_request_reply() { + hcibuf[0] = 0x0E; // HCI OCF = 0E + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x06; // parameter length 6 + hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr + hcibuf[4] = disc_bdaddr[1]; + hcibuf[5] = disc_bdaddr[2]; + hcibuf[6] = disc_bdaddr[3]; + hcibuf[7] = disc_bdaddr[4]; + hcibuf[8] = disc_bdaddr[5]; + + HCI_Command(hcibuf, 9); +} +void BTD::hci_link_key_request_negative_reply() { + hcibuf[0] = 0x0C; // HCI OCF = 0C + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x06; // parameter length 6 + hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr + hcibuf[4] = disc_bdaddr[1]; + hcibuf[5] = disc_bdaddr[2]; + hcibuf[6] = disc_bdaddr[3]; + hcibuf[7] = disc_bdaddr[4]; + hcibuf[8] = disc_bdaddr[5]; + + HCI_Command(hcibuf, 9); +} +void BTD::hci_disconnect(uint16_t handle) { // This is called by the different services + hci_event_flag &= ~HCI_FLAG_DISCONN_COMPLETE; + hcibuf[0] = 0x06; // HCI OCF = 6 + hcibuf[1] = 0x01 << 2; // HCI OGF = 1 + hcibuf[2] = 0x03; // parameter length = 3 + hcibuf[3] = (uint8_t)(handle & 0xFF);//connection handle - low byte + hcibuf[4] = (uint8_t)((handle >> 8) & 0x0F);//connection handle - high byte + hcibuf[5] = 0x13; // reason + + HCI_Command(hcibuf, 6); +} +/******************************************************************* + * * + * HCI ACL Data Packet * + * * + * buf[0] buf[1] buf[2] buf[3] + * 0 4 8 11 12 16 24 31 MSB + * .-+-+-+-+-+-+-+-|-+-+-+-|-+-|-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * | HCI Handle |PB |BC | Data Total Length | HCI ACL Data Packet + * .-+-+-+-+-+-+-+-|-+-+-+-|-+-|-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * + * buf[4] buf[5] buf[6] buf[7] + * 0 8 16 31 MSB + * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * | Length | Channel ID | Basic L2CAP header + * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * + * buf[8] buf[9] buf[10] buf[11] + * 0 8 16 31 MSB + * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. + * | Code | Identifier | Length | Control frame (C-frame) + * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. (signaling packet format) + */ +/************************************************************/ +/* L2CAP Commands */ +/************************************************************/ +void BTD::L2CAP_Command(uint16_t handle, uint8_t* data, uint8_t nbytes, uint8_t channelLow, uint8_t channelHigh) { + uint8_t buf[256]; + buf[0] = (uint8_t)(handle & 0xff); // HCI handle with PB,BC flag + buf[1] = (uint8_t)(((handle >> 8) & 0x0f) | 0x20); + buf[2] = (uint8_t)((4 + nbytes) & 0xff); // HCI ACL total data length + buf[3] = (uint8_t)((4 + nbytes) >> 8); + buf[4] = (uint8_t)(nbytes & 0xff); // L2CAP header: Length + buf[5] = (uint8_t)(nbytes >> 8); + buf[6] = channelLow; + buf[7] = channelHigh; + + for (uint16_t i = 0; i < nbytes; i++) // L2CAP C-frame + buf[8 + i] = data[i]; + + uint8_t rcode = pUsb->outTransfer(bAddress, epInfo[ BTD_DATAOUT_PIPE ].epAddr, (8 + nbytes), buf); + if(rcode) { +#ifdef DEBUG + Notify(PSTR("\r\nError sending L2CAP message: 0x")); + PrintHex(rcode); + Notify(PSTR(" - Channel ID: ")); + Serial.print(channelHigh); + Notify(PSTR(" ")); + Serial.print(channelLow); +#endif + } +} +void BTD::l2cap_connection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid, uint8_t result) { + l2capoutbuf[0] = L2CAP_CMD_CONNECTION_RESPONSE; // Code + l2capoutbuf[1] = rxid; // Identifier + l2capoutbuf[2] = 0x08; // Length + l2capoutbuf[3] = 0x00; + l2capoutbuf[4] = dcid[0]; // Destination CID + l2capoutbuf[5] = dcid[1]; + l2capoutbuf[6] = scid[0]; // Source CID + l2capoutbuf[7] = scid[1]; + l2capoutbuf[8] = result; // Result: Pending or Success + l2capoutbuf[9] = 0x00; + l2capoutbuf[10] = 0x00; // No further information + l2capoutbuf[11] = 0x00; + + L2CAP_Command(handle, l2capoutbuf, 12); +} +void BTD::l2cap_config_request(uint16_t handle, uint8_t rxid, uint8_t* dcid) { + l2capoutbuf[0] = L2CAP_CMD_CONFIG_REQUEST; // Code + l2capoutbuf[1] = rxid; // Identifier + l2capoutbuf[2] = 0x08; // Length + l2capoutbuf[3] = 0x00; + l2capoutbuf[4] = dcid[0]; // Destination CID + l2capoutbuf[5] = dcid[1]; + l2capoutbuf[6] = 0x00; // Flags + l2capoutbuf[7] = 0x00; + l2capoutbuf[8] = 0x01; // Config Opt: type = MTU (Maximum Transmission Unit) - Hint + l2capoutbuf[9] = 0x02; // Config Opt: length + l2capoutbuf[10] = 0xFF; // MTU + l2capoutbuf[11] = 0xFF; + + L2CAP_Command(handle, l2capoutbuf, 12); +} +void BTD::l2cap_config_response(uint16_t handle, uint8_t rxid, uint8_t* scid) { + l2capoutbuf[0] = L2CAP_CMD_CONFIG_RESPONSE; // Code + l2capoutbuf[1] = rxid; // Identifier + l2capoutbuf[2] = 0x0A; // Length + l2capoutbuf[3] = 0x00; + l2capoutbuf[4] = scid[0]; // Source CID + l2capoutbuf[5] = scid[1]; + l2capoutbuf[6] = 0x00; // Flag + l2capoutbuf[7] = 0x00; + l2capoutbuf[8] = 0x00; // Result + l2capoutbuf[9] = 0x00; + l2capoutbuf[10] = 0x01; // Config + l2capoutbuf[11] = 0x02; + l2capoutbuf[12] = 0xA0; + l2capoutbuf[13] = 0x02; + + L2CAP_Command(handle, l2capoutbuf, 14); +} +void BTD::l2cap_disconnection_request(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid) { + l2capoutbuf[0] = L2CAP_CMD_DISCONNECT_REQUEST; // Code + l2capoutbuf[1] = rxid; // Identifier + l2capoutbuf[2] = 0x04; // Length + l2capoutbuf[3] = 0x00; + l2capoutbuf[4] = dcid[0]; + l2capoutbuf[5] = dcid[1]; + l2capoutbuf[6] = scid[0]; + l2capoutbuf[7] = scid[1]; + L2CAP_Command(handle, l2capoutbuf, 8); +} +void BTD::l2cap_disconnection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid) { + l2capoutbuf[0] = L2CAP_CMD_DISCONNECT_RESPONSE; // Code + l2capoutbuf[1] = rxid; // Identifier + l2capoutbuf[2] = 0x04; // Length + l2capoutbuf[3] = 0x00; + l2capoutbuf[4] = dcid[0]; + l2capoutbuf[5] = dcid[1]; + l2capoutbuf[6] = scid[0]; + l2capoutbuf[7] = scid[1]; + L2CAP_Command(handle, l2capoutbuf, 8); +} +void BTD::l2cap_information_response(uint16_t handle, uint8_t rxid, uint8_t infoTypeLow, uint8_t infoTypeHigh) { + l2capoutbuf[0] = L2CAP_CMD_INFORMATION_RESPONSE; // Code + l2capoutbuf[1] = rxid; // Identifier + l2capoutbuf[2] = 0x08; // Length + l2capoutbuf[3] = 0x00; + l2capoutbuf[4] = infoTypeLow; + l2capoutbuf[5] = infoTypeHigh; + l2capoutbuf[6] = 0x00; // Result = success + l2capoutbuf[7] = 0x00; // Result = success + l2capoutbuf[8] = 0x00; + l2capoutbuf[9] = 0x00; + l2capoutbuf[10] = 0x00; + l2capoutbuf[11] = 0x00; + L2CAP_Command(handle, l2capoutbuf, 12); +} + +/* PS3 Commands - only set Bluetooth address is implemented */ +void BTD::setBdaddr(uint8_t* BDADDR) +{ + /* Set the internal bluetooth address */ + uint8_t buf[8]; + buf[0] = 0x01; + buf[1] = 0x00; + for (uint8_t i = 0; i < 6; i++) + buf[i+2] = BDADDR[5 - i];//Copy into buffer, has to be written reversed + + //bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0xF5), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data) + pUsb->ctrlReq(bAddress,epInfo[BTD_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0xF5, 0x03, 0x00, 8, 8, buf, NULL); +#ifdef DEBUG + Notify(PSTR("\r\nBluetooth Address was set to: ")); + for(int8_t i = 5; i > 0; i--) + { + PrintHex(my_bdaddr[i]); + Serial.print(":"); + } + PrintHex(my_bdaddr[0]); +#endif + return; +} +void BTD::setMoveBdaddr(uint8_t* BDADDR) +{ + /* Set the internal bluetooth address */ + uint8_t buf[11]; + buf[0] = 0x05; + buf[7] = 0x10; + buf[8] = 0x01; + buf[9] = 0x02; + buf[10] = 0x12; + + for (uint8_t i = 0; i < 6; i++) + buf[i + 1] = BDADDR[i]; + + //bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x05), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data) + pUsb->ctrlReq(bAddress,epInfo[BTD_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x05, 0x03, 0x00,11,11, buf, NULL); +#ifdef DEBUG + Notify(PSTR("\r\nBluetooth Address was set to: ")); + for(int8_t i = 5; i > 0; i--) + { + PrintHex(my_bdaddr[i]); + Serial.print(":"); + } + PrintHex(my_bdaddr[0]); +#endif + return; +} \ No newline at end of file diff --git a/BTD.h b/BTD.h new file mode 100644 index 00000000..63c28d9c --- /dev/null +++ b/BTD.h @@ -0,0 +1,244 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#ifndef _btd_h_ +#define _btd_h_ + +#define PS3_CONTROL_PIPE 0 +#define PS3_OUTPUT_PIPE 1 +#define PS3_INPUT_PIPE 2 + +#define PS3_CONTROL_PIPE 0 +#define PS3_OUTPUT_PIPE 1 +#define PS3_INPUT_PIPE 2 + +#define EP_INTERRUPT 0x03 +#define EP_MAXPKTSIZE 64 // max size for data via USB + +#include "Usb.h" +#include "confdescparser.h" + +//PID and VID of the Sony PS3 devices +#define PS3_VID 0x054C // Sony Corporation +#define PS3_PID 0x0268 // PS3 Controller DualShock 3 +#define PS3NAVIGATION_PID 0x042F // Navigation controller +#define PS3MOVE_PID 0x03D5 // Motion controller + +/* Bluetooth dongle data taken from descriptors */ +#define BULK_MAXPKTSIZE 64 // max size for ACL data + +// Used in control endpoint header for HCI Commands +#define bmREQ_HCI_OUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE +// Used in control endpoint header for HID Commands +#define bmREQ_HID_OUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE +#define HID_REQUEST_SET_REPORT 0x09 + +/* Bluetooth HCI states for hci_task() */ +#define HCI_INIT_STATE 0 +#define HCI_RESET_STATE 1 +#define HCI_BDADDR_STATE 2 +#define HCI_LOCAL_VERSION_STATE 3 +#define HCI_SET_NAME_STATE 4 +#define HCI_SCANNING_STATE 5 +#define HCI_CONNECT_IN_STATE 6 +#define HCI_REMOTE_NAME_STATE 7 +#define HCI_CONNECTED_STATE 8 +#define HCI_DISABLE_SCAN_STATE 9 +#define HCI_DONE_STATE 10 +#define HCI_DISCONNECT_STATE 11 + +/* HCI event flags*/ +#define HCI_FLAG_CMD_COMPLETE 0x01 +#define HCI_FLAG_CONN_COMPLETE 0x02 +#define HCI_FLAG_DISCONN_COMPLETE 0x04 +#define HCI_FLAG_REMOTE_NAME_COMPLETE 0x08 +#define HCI_FLAG_INCOMING_REQUEST 0x10 +#define HCI_FLAG_READ_BDADDR 0x20 +#define HCI_FLAG_READ_VERSION 0x40 + +/*Macros for HCI event flag tests */ +#define hci_cmd_complete (hci_event_flag & HCI_FLAG_CMD_COMPLETE) +#define hci_connect_complete (hci_event_flag & HCI_FLAG_CONN_COMPLETE) +#define hci_disconnect_complete (hci_event_flag & HCI_FLAG_DISCONN_COMPLETE) +#define hci_remote_name_complete (hci_event_flag & HCI_FLAG_REMOTE_NAME_COMPLETE) +#define hci_incoming_connect_request (hci_event_flag & HCI_FLAG_INCOMING_REQUEST) +#define hci_read_bdaddr_complete (hci_event_flag & HCI_FLAG_READ_BDADDR) +#define hci_read_version_complete (hci_event_flag & HCI_FLAG_READ_VERSION) + +/* HCI Events managed */ +#define EV_CONNECT_COMPLETE 0x03 +#define EV_INCOMING_CONNECT 0x04 +#define EV_DISCONNECT_COMPLETE 0x05 +#define EV_AUTHENTICATION_COMPLETE 0x06 +#define EV_REMOTE_NAME_COMPLETE 0x07 +#define EV_ENCRYPTION_CHANGE 0x08 +#define EV_CHANGE_CONNECTION_LINK 0x09 +#define EV_ROLE_CHANGED 0x12 +#define EV_NUM_COMPLETE_PKT 0x13 +#define EV_PIN_CODE_REQUEST 0x16 +#define EV_LINK_KEY_REQUEST 0x17 +#define EV_LINK_KEY_NOTIFICATION 0x18 +#define EV_DATA_BUFFER_OVERFLOW 0x1A +#define EV_MAX_SLOTS_CHANGE 0x1B +#define EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE 0x0C +#define EV_QOS_SETUP_COMPLETE 0x0D +#define EV_COMMAND_COMPLETE 0x0E +#define EV_COMMAND_STATUS 0x0F +#define EV_LOOPBACK_COMMAND 0x19 +#define EV_PAGE_SCAN_REP_MODE 0x20 + +/* L2CAP signaling commands */ +#define L2CAP_CMD_COMMAND_REJECT 0x01 +#define L2CAP_CMD_CONNECTION_REQUEST 0x02 +#define L2CAP_CMD_CONNECTION_RESPONSE 0x03 +#define L2CAP_CMD_CONFIG_REQUEST 0x04 +#define L2CAP_CMD_CONFIG_RESPONSE 0x05 +#define L2CAP_CMD_DISCONNECT_REQUEST 0x06 +#define L2CAP_CMD_DISCONNECT_RESPONSE 0x07 +#define L2CAP_CMD_INFORMATION_REQUEST 0x0A +#define L2CAP_CMD_INFORMATION_RESPONSE 0x0B + +// Used For Connection Response - Remember to Include High Byte +#define PENDING 0x01 +#define SUCCESSFUL 0x00 + +// Used to determine if it is a Bluetooth dongle +#define WI_SUBCLASS_RF 0x01 // RF Controller +#define WI_PROTOCOL_BT 0x01 // Bluetooth Programming Interface + +#define BTD_MAX_ENDPOINTS 4 +#define BTD_NUMDEVICES 7 // Max number of Bluetooth devices + +class BluetoothService { // All services should include this class +public: + virtual void ACLData(uint8_t* ACLData); // Used to pass acldata to the services + virtual void Poll(); // Used to run the different state machines + virtual void Release(); // Used to disconnect both the L2CAP Channel and the HCI Connection + virtual void disconnect(); // Used to disconnect both the L2CAP Channel and the HCI Connection +}; + +class BTD : public USBDeviceConfig, public UsbConfigXtracter { +public: + BTD(USB *p); // Constructor + + // USBDeviceConfig implementation + virtual uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); + virtual uint8_t Release(); + virtual uint8_t Poll(); + virtual uint8_t GetAddress() { return bAddress; }; + virtual bool isReady() { return bPollEnable; }; + + // UsbConfigXtracter implementation, used to extract endpoint information + virtual void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep); + + bool watingForConnection; // Use this to see if it is waiting for a incoming connection + void disconnect() { // Used this void to disconnect all services + for (uint8_t i=0; idisconnect(); // Disconnect both the L2CAP Channel and the HCI Connection + }; + + /* Register bluetooth dongle members/services */ + uint8_t registerServiceClass(BluetoothService *pService) { + for (uint8_t i=0; iregisterServiceClass(this); // Register it as a Bluetooth service - if (pUsb) // register in USB subsystem - pUsb->RegisterDeviceClass(this); //set devConfig[] entry + pBtd->my_bdaddr[5] = btadr5; // Change to your dongle's Bluetooth address instead + pBtd->my_bdaddr[4] = btadr4; + pBtd->my_bdaddr[3] = btadr3; + pBtd->my_bdaddr[2] = btadr2; + pBtd->my_bdaddr[1] = btadr1; + pBtd->my_bdaddr[0] = btadr0; + + HIDBuffer[0] = 0x52;// HID BT Set_report (0x50) | Report Type (Output 0x02) + HIDBuffer[1] = 0x01;// Report ID - my_bdaddr[5] = btadr5; // Change to your dongle's Bluetooth address instead - my_bdaddr[4] = btadr4; - my_bdaddr[3] = btadr3; - my_bdaddr[2] = btadr2; - my_bdaddr[1] = btadr1; - my_bdaddr[0] = btadr0; -} - -uint8_t PS3BT::Init(uint8_t parent, uint8_t port, bool lowspeed) -{ - uint8_t buf[sizeof(USB_DEVICE_DESCRIPTOR)]; - uint8_t rcode; - UsbDevice *p = NULL; - EpInfo *oldep_ptr = NULL; - uint8_t num_of_conf; // number of configurations - uint16_t PID; - uint16_t VID; + //Needed for PS3 Move Controller commands to work via bluetooth + HIDMoveBuffer[0] = 0xA2;// HID BT DATA_request (0xA0) | Report Type (Output 0x02) + HIDMoveBuffer[1] = 0x02;// Report ID - // get memory address of USB device address pool - AddressPool &addrPool = pUsb->GetAddressPool(); -#ifdef EXTRADEBUG - Notify(PSTR("\r\nPS3BT Init")); -#endif - // check if address has already been assigned to an instance - if (bAddress) - { -#ifdef DEBUG - Notify(PSTR("\r\nAddress in use")); -#endif - return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; - } + /* Set device cid for the control and intterrupt channelse - LSB */ + control_dcid[0] = 0x40;//0x0040 + control_dcid[1] = 0x00; + interrupt_dcid[0] = 0x41;//0x0041 + interrupt_dcid[1] = 0x00; - // Get pointer to pseudo device with address 0 assigned - p = addrPool.GetUsbDevicePtr(0); - - if (!p) - { -#ifdef DEBUG - Notify(PSTR("\r\nAddress not found")); -#endif - return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; - } - - if (!p->epinfo) - { -#ifdef DEBUG - Notify(PSTR("\r\nepinfo is null")); -#endif - return USB_ERROR_EPINFO_IS_NULL; - } - - // Save old pointer to EP_RECORD of address 0 - oldep_ptr = p->epinfo; - - // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence - p->epinfo = epInfo; - - p->lowspeed = lowspeed; - - // Get device descriptor - rcode = pUsb->getDevDescr(0, 0, sizeof(USB_DEVICE_DESCRIPTOR), (uint8_t*)buf);// Get device descriptor - addr, ep, nbytes, data - - // Restore p->epinfo - p->epinfo = oldep_ptr; - - if(rcode) - goto FailGetDevDescr; - - // Allocate new address according to device class - bAddress = addrPool.AllocAddress(parent, false, port); - - if (!bAddress) - return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; - - // Extract Max Packet Size from device descriptor - epInfo[0].maxPktSize = (uint8_t)((USB_DEVICE_DESCRIPTOR*)buf)->bMaxPacketSize0; - - // Assign new address to the device - rcode = pUsb->setAddr( 0, 0, bAddress ); - if (rcode) - { - p->lowspeed = false; - addrPool.FreeAddress(bAddress); - bAddress = 0; -#ifdef DEBUG - Notify(PSTR("\r\nsetAddr: ")); -#endif - PrintHex(rcode); - return rcode; - } -#ifdef EXTRADEBUG - Notify(PSTR("\r\nAddr: ")); - PrintHex(bAddress); -#endif - p->lowspeed = false; - - //get pointer to assigned address record - p = addrPool.GetUsbDevicePtr(bAddress); - if (!p) - return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; - - p->lowspeed = lowspeed; - - // Assign epInfo to epinfo pointer - only EP0 is known - rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); - if (rcode) - goto FailSetDevTblEntry; - - num_of_conf = ((USB_DEVICE_DESCRIPTOR*)buf)->bNumConfigurations; - VID = ((USB_DEVICE_DESCRIPTOR*)buf)->idVendor; - PID = ((USB_DEVICE_DESCRIPTOR*)buf)->idProduct; - - if(VID == PS3_VID && (PID == PS3_PID || PID == PS3NAVIGATION_PID || PID == PS3MOVE_PID)) { - /* The application will work in reduced host mode, so we can save program and data - memory space. After verifying the PID and VID we will use known values for the - configuration values for device, interface, endpoints and HID for the PS3 Controllers */ - - /* Initialize data structures for endpoints of device */ - epInfo[ PS3_OUTPUT_PIPE ].epAddr = 0x02; // PS3 output endpoint - epInfo[ PS3_OUTPUT_PIPE ].epAttribs = EP_INTERRUPT; - epInfo[ PS3_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints - epInfo[ PS3_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE; - epInfo[ PS3_OUTPUT_PIPE ].bmSndToggle = bmSNDTOG0; - epInfo[ PS3_OUTPUT_PIPE ].bmRcvToggle = bmRCVTOG0; - epInfo[ PS3_INPUT_PIPE ].epAddr = 0x01; // PS3 report endpoint - epInfo[ PS3_INPUT_PIPE ].epAttribs = EP_INTERRUPT; - epInfo[ PS3_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints - epInfo[ PS3_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE; - epInfo[ PS3_INPUT_PIPE ].bmSndToggle = bmSNDTOG0; - epInfo[ PS3_INPUT_PIPE ].bmRcvToggle = bmRCVTOG0; - - rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo); - if( rcode ) - goto FailSetDevTblEntry; - - delay(200);//Give time for address change - - rcode = pUsb->setConf(bAddress, epInfo[ PS3_CONTROL_PIPE ].epAddr, 1); - if( rcode ) - goto FailSetConf; - - if(PID == PS3_PID || PID == PS3NAVIGATION_PID) - { - if(PID == PS3_PID) { -#ifdef DEBUG - Notify(PSTR("\r\nDualshock 3 Controller Connected")); -#endif - } else { // must be a navigation controller -#ifdef DEBUG - Notify(PSTR("\r\nNavigation Controller Connected")); -#endif - } - /* Set internal bluetooth address */ - setBdaddr(my_bdaddr); - } - else // must be a Motion controller - { -#ifdef DEBUG - Notify(PSTR("\r\nMotion Controller Connected")); -#endif - setMoveBdaddr(my_bdaddr); - } - } - else - { - //check if attached device is a Bluetooth dongle and fill endpoint data structure - //first interface in the configuration must have Bluetooth assigned Class/Subclass/Protocol - //and 3 endpoints - interrupt-IN, bulk-IN, bulk-OUT, - //not necessarily in this order - for (uint8_t i=0; i confDescrParser(this); - rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser); - if(rcode) - goto FailGetConfDescr; - if(bNumEP > 3) //all endpoints extracted - break; - } // for (uint8_t i=0; isetEpInfoEntry(bAddress, bNumEP, epInfo); - if(rcode) - goto FailSetDevTblEntry; - - delay(200); // Give time for address change - - // Set Configuration Value - rcode = pUsb->setConf(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, bConfNum); - if(rcode) - goto FailSetConf; - - //Needed for PS3 Dualshock Controller commands to work via bluetooth - for (uint8_t i = 0; i < OUTPUT_REPORT_BUFFER_SIZE; i++) - HIDBuffer[i + 2] = pgm_read_byte(&OUTPUT_REPORT_BUFFER[i]);//First two bytes reserved for report type and ID - - HIDBuffer[0] = 0x52;// HID BT Set_report (0x50) | Report Type (Output 0x02) - HIDBuffer[1] = 0x01;// Report ID - - //Needed for PS3 Move Controller commands to work via bluetooth - HIDMoveBuffer[0] = 0xA2;// HID BT DATA_request (0xA0) | Report Type (Output 0x02) - HIDMoveBuffer[1] = 0x02;// Report ID - - /* Set device cid for the control and intterrupt channelse - LSB */ - control_dcid[0] = 0x40;//0x0040 - control_dcid[1] = 0x00; - interrupt_dcid[0] = 0x41;//0x0041 - interrupt_dcid[1] = 0x00; - - hci_num_reset_loops = 100; // only loop 100 times before trying to send the hci reset command - - hci_state = HCI_INIT_STATE; - hci_counter = 0; - l2cap_state = L2CAP_EV_WAIT; -#ifdef DEBUG - Notify(PSTR("\r\nBluetooth Dongle Initialized")); -#endif - - watingForConnection = false; - bPollEnable = true; - } - return 0; //successful configuration - - /* diagnostic messages */ -FailGetDevDescr: -#ifdef DEBUG - Notify(PSTR("\r\ngetDevDescr:")); -#endif - goto Fail; -FailSetDevTblEntry: -#ifdef DEBUG - Notify(PSTR("\r\nsetDevTblEn:")); -#endif - goto Fail; -FailGetConfDescr: -#ifdef DEBUG - Notify(PSTR("\r\ngetConf:")); -#endif - goto Fail; -FailSetConf: -#ifdef DEBUG - Notify(PSTR("\r\nsetConf:")); -#endif - goto Fail; -FailUnknownDevice: -#ifdef DEBUG - Notify(PSTR("\r\nUnknown Device Connected - VID: ")); - PrintHex(VID); - Notify(PSTR(" PID: ")); - PrintHex(PID); -#endif - goto Fail; -Fail: -#ifdef DEBUG - Notify(PSTR("\r\nPS3 Init Failed, error code: ")); - Serial.print(rcode); -#endif Release(); - return rcode; -} -/* Extracts interrupt-IN, bulk-IN, bulk-OUT endpoint information from config descriptor */ -void PS3BT::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) -{ - //ErrorMessage(PSTR("Conf.Val"),conf); - //ErrorMessage(PSTR("Iface Num"),iface); - //ErrorMessage(PSTR("Alt.Set"),alt); - - if(alt) // wrong interface - by BT spec, no alt setting - return; - - bConfNum = conf; - uint8_t index; - - if ((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80) //Interrupt In endpoint found - index = BTD_EVENT_PIPE; - - else { - if ((pep->bmAttributes & 0x02) == 2) //bulk endpoint found - index = ((pep->bEndpointAddress & 0x80) == 0x80) ? BTD_DATAIN_PIPE : BTD_DATAOUT_PIPE; - else - return; - } - - //Fill the rest of endpoint data structure - epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F); - epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize; -#ifdef EXTRADEBUG - PrintEndpointDescriptor(pep); -#endif - if(pollInterval < pep->bInterval) // Set the polling interval as the largest polling interval obtained from endpoints - pollInterval = pep->bInterval; - bNumEP++; - return; -} -void PS3BT::PrintEndpointDescriptor( const USB_ENDPOINT_DESCRIPTOR* ep_ptr ) -{ - Notify(PSTR("\r\nEndpoint descriptor:")); - Notify(PSTR("\r\nLength:\t\t")); - PrintHex(ep_ptr->bLength); - Notify(PSTR("\r\nType:\t\t")); - PrintHex(ep_ptr->bDescriptorType); - Notify(PSTR("\r\nAddress:\t")); - PrintHex(ep_ptr->bEndpointAddress); - Notify(PSTR("\r\nAttributes:\t")); - PrintHex(ep_ptr->bmAttributes); - Notify(PSTR("\r\nMaxPktSize:\t")); - PrintHex(ep_ptr->wMaxPacketSize); - Notify(PSTR("\r\nPoll Intrv:\t")); - PrintHex(ep_ptr->bInterval); -} - -/* Performs a cleanup after failed Init() attempt */ -uint8_t PS3BT::Release() -{ - PS3Connected = false; - PS3MoveConnected = false; - PS3NavigationConnected = false; - pUsb->GetAddressPool().FreeAddress(bAddress); - bAddress = 0; - bPollEnable = false; - bNumEP = 1; // must have to be reset to 1 - return 0; -} -uint8_t PS3BT::Poll() -{ - if (!bPollEnable) - return 0; - if (qNextPollTime <= millis()) { // Don't poll if shorter than polling interval - qNextPollTime = millis() + pollInterval; // Set new poll time - HCI_event_task(); // poll the HCI event pipe - ACL_event_task(); // start polling the ACL input pipe too, though discard data until connected - } - return 0; -} -void PS3BT::setBdaddr(uint8_t* BDADDR) -{ - /* Set the internal bluetooth address */ - uint8_t buf[8]; - buf[0] = 0x01; - buf[1] = 0x00; - for (uint8_t i = 0; i < 6; i++) - buf[i+2] = BDADDR[5 - i];//Copy into buffer, has to be written reversed - - //bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0xF5), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data) - pUsb->ctrlReq(bAddress,epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0xF5, 0x03, 0x00, 8, 8, buf, NULL); -#ifdef DEBUG - Notify(PSTR("\r\nBluetooth Address was set to: ")); - for(int8_t i = 5; i > 0; i--) - { - PrintHex(my_bdaddr[i]); - Serial.print(":"); - } - PrintHex(my_bdaddr[0]); -#endif - return; -} -void PS3BT::setMoveBdaddr(uint8_t* BDADDR) -{ - /* Set the internal bluetooth address */ - uint8_t buf[11]; - buf[0] = 0x05; - buf[7] = 0x10; - buf[8] = 0x01; - buf[9] = 0x02; - buf[10] = 0x12; - - for (uint8_t i = 0; i < 6; i++) - buf[i + 1] = BDADDR[i]; - - //bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x05), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data) - pUsb->ctrlReq(bAddress,epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x05, 0x03, 0x00,11,11, buf, NULL); -#ifdef DEBUG - Notify(PSTR("\r\nBluetooth Address was set to: ")); - for(int8_t i = 5; i > 0; i--) - { - PrintHex(my_bdaddr[i]); - Serial.print(":"); - } - PrintHex(my_bdaddr[0]); -#endif - return; } bool PS3BT::getButton(Button b) { - if (l2capinbuf == NULL) + if (l2capinpointer == NULL) return false; if(PS3MoveConnected) { - if((l2capinbuf[((uint16_t)b >> 8)-1] & ((uint8_t)b & 0xff))) // All the buttons locations are shifted one back on the Move controller + if((l2capinpointer[((uint16_t)b >> 8)-1] & ((uint8_t)b & 0xff))) // All the buttons locations are shifted one back on the Move controller return true; else return false; } else { - if((l2capinbuf[(uint16_t)b >> 8] & ((uint8_t)b & 0xff))) + if((l2capinpointer[(uint16_t)b >> 8] & ((uint8_t)b & 0xff))) return true; else return false; @@ -455,41 +78,41 @@ bool PS3BT::getButton(Button b) } uint8_t PS3BT::getAnalogButton(AnalogButton a) { - if (l2capinbuf == NULL) + if (l2capinpointer == NULL) return 0; - return (uint8_t)(l2capinbuf[(uint16_t)a]); + return (uint8_t)(l2capinpointer[(uint16_t)a]); } uint8_t PS3BT::getAnalogHat(AnalogHat a) { - if (l2capinbuf == NULL) + if (l2capinpointer == NULL) return 0; - return (uint8_t)(l2capinbuf[(uint16_t)a]); + return (uint8_t)(l2capinpointer[(uint16_t)a]); } int16_t PS3BT::getSensor(Sensor a) { - if (l2capinbuf == NULL) + if (l2capinpointer == NULL) return 0; if (a == aX || a == aY || a == aZ || a == gZ) - return ((l2capinbuf[(uint16_t)a] << 8) | l2capinbuf[(uint16_t)a + 1]); + return ((l2capinpointer[(uint16_t)a] << 8) | l2capinpointer[(uint16_t)a + 1]); else if (a == mXmove || a == mYmove || a == mZmove) // These are all 12-bits long { // Might not be correct, haven't tested it yet /*if (a == mXmove) - return ((l2capinbuf[(uint16_t)a + 1] << 0x04) | (l2capinbuf[(uint16_t)a] << 0x0C)); + return ((l2capinpointer[(uint16_t)a + 1] << 0x04) | (l2capinpointer[(uint16_t)a] << 0x0C)); else if (a == mYmove) - return ((l2capinbuf[(uint16_t)a + 1] & 0xF0) | (l2capinbuf[(uint16_t)a] << 0x08)); + return ((l2capinpointer[(uint16_t)a + 1] & 0xF0) | (l2capinpointer[(uint16_t)a] << 0x08)); else if (a == mZmove) - return ((l2capinbuf[(uint16_t)a + 1] << 0x0F) | (l2capinbuf[(uint16_t)a] << 0x0C)); + return ((l2capinpointer[(uint16_t)a + 1] << 0x0F) | (l2capinpointer[(uint16_t)a] << 0x0C)); */ if (a == mXmove || a == mYmove) - return (((l2capinbuf[(uint16_t)a] & 0x0F) << 8) | (l2capinbuf[(uint16_t)a + 1])); + return (((l2capinpointer[(uint16_t)a] & 0x0F) << 8) | (l2capinpointer[(uint16_t)a + 1])); else // mZmove - return ((l2capinbuf[(uint16_t)a] << 4) | (l2capinbuf[(uint16_t)a + 1] >> 4)); + return ((l2capinpointer[(uint16_t)a] << 4) | (l2capinpointer[(uint16_t)a + 1] >> 4)); } else if (a == tempMove) // The tempearature is 12 bits long too - return ((l2capinbuf[(uint16_t)a] << 4) | ((l2capinbuf[(uint16_t)a + 1] & 0xF0) >> 4)); + return ((l2capinpointer[(uint16_t)a] << 4) | ((l2capinpointer[(uint16_t)a + 1] & 0xF0) >> 4)); else // aXmove, aYmove, aZmove, gXmove, gYmove and gZmove - return (l2capinbuf[(uint16_t)a] | (l2capinbuf[(uint16_t)a + 1] << 8)); + return (l2capinpointer[(uint16_t)a] | (l2capinpointer[(uint16_t)a + 1] << 8)); } double PS3BT::getAngle(Angle a) { double accXval; @@ -536,9 +159,9 @@ String PS3BT::getTemperature() { } bool PS3BT::getStatus(Status c) { - if (l2capinbuf == NULL) + if (l2capinpointer == NULL) return false; - if (l2capinbuf[(uint16_t)c >> 8] == ((uint8_t)c & 0xff)) + if (l2capinpointer[(uint16_t)c >> 8] == ((uint8_t)c & 0xff)) return true; return false; } @@ -594,333 +217,51 @@ String PS3BT::getStatusString() return statusOutput; } } -void PS3BT::disconnect()//Use this void to disconnect any of the controllers -{ - if (PS3Connected) - PS3Connected = false; - else if (PS3MoveConnected) - PS3MoveConnected = false; - else if (PS3NavigationConnected) - PS3NavigationConnected = false; +void PS3BT::Release() { + PS3Connected = false; + PS3MoveConnected = false; + PS3NavigationConnected = false; + l2cap_event_flag = 0; // Reset flags + l2cap_state = L2CAP_EV_WAIT; + + // Needed for PS3 Dualshock Controller commands to work via bluetooth + for (uint8_t i = 0; i < OUTPUT_REPORT_BUFFER_SIZE; i++) + HIDBuffer[i + 2] = pgm_read_byte(&OUTPUT_REPORT_BUFFER[i]); // First two bytes reserved for report type and ID +} + +void PS3BT::disconnect() { //Use this void to disconnect any of the controllers + PS3Connected = false; + PS3MoveConnected = false; + PS3NavigationConnected = false; //First the HID interrupt channel has to be disconencted, then the HID control channel and finally the HCI connection - l2cap_disconnection_request(0x0A, interrupt_dcid, interrupt_scid); + pBtd->l2cap_disconnection_request(hci_handle,0x0A, interrupt_scid, interrupt_dcid); + l2cap_event_flag = 0; // Reset flags l2cap_state = L2CAP_EV_INTERRUPT_DISCONNECT; } -void PS3BT::HCI_event_task() -{ - /* check the event pipe*/ - uint16_t MAX_BUFFER_SIZE = BULK_MAXPKTSIZE; // Request more than 16 bytes anyway, the inTransfer routine will take care of this - uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ BTD_EVENT_PIPE ].epAddr, &MAX_BUFFER_SIZE, hcibuf); // input on endpoint 1 - if(!rcode || rcode == hrNAK) // Check for errors - { - switch (hcibuf[0]) //switch on event type - { - case EV_COMMAND_COMPLETE: - hci_event_flag |= HCI_FLAG_CMD_COMPLETE; // set command complete flag - if (!hcibuf[5]) { // check if command succeeded - if((hcibuf[3] == 0x01) && (hcibuf[4] == 0x10)) { // parameters from read local version information - hci_version = hcibuf[6]; // Check if it supports 2.0+EDR - see http://www.bluetooth.org/Technical/AssignedNumbers/hci.htm - hci_event_flag |= HCI_FLAG_READ_VERSION; - } - else if((hcibuf[3] == 0x09) && (hcibuf[4] == 0x10)) { // parameters from read local bluetooth address - for (uint8_t i = 0; i < 6; i++) - my_bdaddr[i] = hcibuf[6 + i]; - hci_event_flag |= HCI_FLAG_READ_BDADDR; - } - } - break; - - case EV_COMMAND_STATUS: - if(hcibuf[2]) // show status on serial if not OK - { +void PS3BT::ACLData(uint8_t* l2capinbuf) { + l2capinpointer = l2capinbuf; + if (l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) { + if(((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) || ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_INTR_PSM)) { + if(!pBtd->connectionClaimed && !PS3Connected && !PS3MoveConnected && !PS3NavigationConnected) { + pBtd->claimConnection(); // Claim that the incoming connection belongs to this service + hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection + for(uint8_t i = 0; i < 30; i++) + remote_name[i] = pBtd->remote_name[i]; // Store the remote name for the connection #ifdef DEBUG - Notify(PSTR("\r\nHCI Command Failed: ")); - PrintHex(hcibuf[2]); - Serial.print(" "); - PrintHex(hcibuf[4]); - Serial.print(" "); - PrintHex(hcibuf[5]); -#endif - } - break; - - case EV_CONNECT_COMPLETE: - if (!hcibuf[2]) // check if connected OK - { - hci_handle = hcibuf[3] | hcibuf[4] << 8; //store the handle for the ACL connection - hci_event_flag |= HCI_FLAG_CONN_COMPLETE; // set connection complete flag - } - break; - - case EV_DISCONNECT_COMPLETE: - if (!hcibuf[2]) // check if disconnected OK - { - hci_event_flag |= HCI_FLAG_DISCONN_COMPLETE; //set disconnect commend complete flag - hci_event_flag &= ~HCI_FLAG_CONN_COMPLETE; // clear connection complete flag - } - break; - - case EV_REMOTE_NAME_COMPLETE: - if (!hcibuf[2]) // check if reading is OK - { - for (uint8_t i = 0; i < 30; i++) - remote_name[i] = hcibuf[9 + i]; //store first 30 bytes - hci_event_flag |= HCI_FLAG_REMOTE_NAME_COMPLETE; - } - break; - - case EV_INCOMING_CONNECT: - disc_bdaddr[0] = hcibuf[2]; - disc_bdaddr[1] = hcibuf[3]; - disc_bdaddr[2] = hcibuf[4]; - disc_bdaddr[3] = hcibuf[5]; - disc_bdaddr[4] = hcibuf[6]; - disc_bdaddr[5] = hcibuf[7]; - hci_event_flag |= HCI_FLAG_INCOMING_REQUEST; - break; - - /* We will just ignore the following events */ - - case EV_NUM_COMPLETE_PKT: - break; - - case EV_ROLE_CHANGED: - break; - - case EV_PAGE_SCAN_REP_MODE: - break; - - case EV_LOOPBACK_COMMAND: - break; - - case EV_DATA_BUFFER_OVERFLOW: - break; - - case EV_CHANGE_CONNECTION_LINK: - break; - - case EV_AUTHENTICATION_COMPLETE: - break; - - default: -#ifdef EXTRADEBUG - if(hcibuf[0] != 0x00) - { - Notify(PSTR("\r\nUnmanaged Event: ")); - PrintHex(hcibuf[0]); - } -#endif - break; - } // switch - HCI_task(); - } - else { -#ifdef EXTRADEBUG - Notify(PSTR("\r\nHCI event error: ")); - PrintHex(rcode); -#endif - } -} - -/* Poll Bluetooth and print result */ -void PS3BT::HCI_task() -{ - switch (hci_state){ - case HCI_INIT_STATE: - hci_counter++; - if (hci_counter > hci_num_reset_loops) // wait until we have looped x times to clear any old events - { - hci_reset(); - hci_state = HCI_RESET_STATE; - hci_counter = 0; - } - break; - - case HCI_RESET_STATE: - hci_counter++; - if (hci_cmd_complete) - { -#ifdef DEBUG - Notify(PSTR("\r\nHCI Reset complete")); -#endif - hci_state = HCI_BDADDR_STATE; - hci_read_bdaddr(); - } - else if (hci_counter > hci_num_reset_loops) - { - hci_num_reset_loops *= 10; - if(hci_num_reset_loops > 2000) - hci_num_reset_loops = 2000; -#ifdef DEBUG - Notify(PSTR("\r\nNo response to HCI Reset")); -#endif - hci_state = HCI_INIT_STATE; - hci_counter = 0; - } - break; - - case HCI_BDADDR_STATE: - if (hci_read_bdaddr_complete) - { -#ifdef DEBUG - Notify(PSTR("\r\nLocal Bluetooth Address: ")); - for(int8_t i = 5; i > 0;i--) - { - PrintHex(my_bdaddr[i]); - Serial.print(":"); - } - PrintHex(my_bdaddr[0]); -#endif - hci_read_local_version_information(); - hci_state = HCI_LOCAL_VERSION_STATE; - } - break; - - case HCI_LOCAL_VERSION_STATE: - if (hci_read_version_complete) - { -#ifdef DEBUG - if(hci_version < 3) { + if(pBtd->hci_version < 3) { // Check the HCI Version of the Bluetooth dongle Notify(PSTR("\r\nYour dongle may not support reading the analog buttons, sensors and status\r\nYour HCI Version is: ")); - Serial.print(hci_version); - Notify(PSTR("\r\nBut should be at least 3\r\nThis means that it doesn't support Bluetooth Version 2.0+EDR")); + Serial.print(pBtd->hci_version); + Notify(PSTR("\r\nBut should be at least 3\r\nThis means that it doesn't support Bluetooth Version 2.0+EDR")); } -#endif - hci_state = HCI_SCANNING_STATE; - } - break; - - break; - case HCI_SCANNING_STATE: -#ifdef DEBUG - Notify(PSTR("\r\nWait For Incoming Connection Request")); -#endif - hci_write_scan_enable(); - watingForConnection = true; - hci_state = HCI_CONNECT_IN_STATE; - break; - - case HCI_CONNECT_IN_STATE: - if(hci_incoming_connect_request) - { - watingForConnection = false; -#ifdef DEBUG - Notify(PSTR("\r\nIncoming Request")); #endif - hci_remote_name(); - hci_state = HCI_REMOTE_NAME_STATE; } - break; - - case HCI_REMOTE_NAME_STATE: - if(hci_remote_name_complete) - { -#ifdef DEBUG - Notify(PSTR("\r\nRemote Name: ")); - for (uint8_t i = 0; i < 30; i++) - { - if(remote_name[i] == NULL) - break; - Serial.write(remote_name[i]); - } -#endif - hci_accept_connection(); - hci_state = HCI_CONNECTED_STATE; - } - break; - - case HCI_CONNECTED_STATE: - if (hci_connect_complete) - { -#ifdef DEBUG - Notify(PSTR("\r\nConnected to Device: ")); - for(int8_t i = 5; i>0;i--) - { - PrintHex(disc_bdaddr[i]); - Serial.print(":"); - } - PrintHex(disc_bdaddr[0]); -#endif - hci_write_scan_disable(); // Only allow one controller - hci_state = HCI_DISABLE_SCAN; - } - break; - - case HCI_DISABLE_SCAN: - if (hci_cmd_complete) - { -#ifdef DEBUG - Notify(PSTR("\r\nScan Disabled")); -#endif - l2cap_event_flag = 0; - l2cap_state = L2CAP_EV_CONTROL_SETUP; - hci_state = HCI_DONE_STATE; - } - break; - - case HCI_DONE_STATE: - if (hci_disconnect_complete) - hci_state = HCI_DISCONNECT_STATE; - break; - - case HCI_DISCONNECT_STATE: - if (hci_disconnect_complete) - { -#ifdef DEBUG - Notify(PSTR("\r\nDisconnected from Device: ")); - for(int8_t i = 5; i>0;i--) - { - PrintHex(disc_bdaddr[i]); - Serial.print(":"); - } - PrintHex(disc_bdaddr[0]); -#endif - l2cap_event_flag = 0; // Clear all flags - hci_event_flag = 0; // Clear all flags - - //Reset all buffers - for (uint8_t i = 0; i < BULK_MAXPKTSIZE; i++) - hcibuf[i] = 0; - for (uint8_t i = 0; i < BULK_MAXPKTSIZE; i++) - l2capinbuf[i] = 0; - for (uint8_t i = 0; i < BULK_MAXPKTSIZE; i++) - l2capoutbuf[i] = 0; - - for (uint8_t i = 0; i < OUTPUT_REPORT_BUFFER_SIZE; i++) - HIDBuffer[i + 2] = pgm_read_byte(&OUTPUT_REPORT_BUFFER[i]); // First two bytes reserved for report type and ID - for (uint8_t i = 0; i < OUTPUT_REPORT_BUFFER_SIZE; i++) - HIDMoveBuffer[i + 2] = 0; // First two bytes reserved for report type and ID - - l2cap_state = L2CAP_EV_WAIT; - hci_state = HCI_SCANNING_STATE; - } - break; - default: - break; + } } -} - -void PS3BT::ACL_event_task() -{ - uint16_t MAX_BUFFER_SIZE = BULK_MAXPKTSIZE; - uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ BTD_DATAIN_PIPE ].epAddr, &MAX_BUFFER_SIZE, l2capinbuf); // input on endpoint 2 - if(!rcode || rcode == hrNAK) // Check for errors - { - if (((l2capinbuf[0] | (l2capinbuf[1] << 8)) == (hci_handle | 0x2000)))//acl_handle_ok - { - if ((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001)//l2cap_control - Channel ID for ACL-U - { - /* - if (l2capinbuf[8] != 0x00) - { - Serial.print("\r\nL2CAP Signaling Command - 0x"); - PrintHex(l2capoutbuf[8]); - } - */ - if (l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) - { + if (((l2capinbuf[0] | (l2capinbuf[1] << 8)) == (hci_handle | 0x2000))) { //acl_handle_ok + if ((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001) { //l2cap_control - Channel ID for ACL-U + if (l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) { #ifdef DEBUG Notify(PSTR("\r\nL2CAP Command Rejected - Reason: ")); PrintHex(l2capinbuf[13]); @@ -936,163 +277,172 @@ void PS3BT::ACL_event_task() PrintHex(l2capinbuf[14]); #endif } - else if (l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) - { - /* - Notify(PSTR("\r\nL2CAP Connection Request - PSM: ")); - PrintHex(l2capinbuf[13]); - Serial.print(" "); - PrintHex(l2capinbuf[12]); - Serial.print(" "); - - Notify(PSTR(" SCID: ")); - PrintHex(l2capinbuf[15]); - Serial.print(" "); - PrintHex(l2capinbuf[14]); - - Notify(PSTR(" Identifier: ")); - PrintHex(l2capinbuf[9]); - */ - if ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == L2CAP_PSM_HID_CTRL) - { + else if (l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nL2CAP Connection Request - PSM: ")); + PrintHex(l2capinbuf[13]); + Notify(PSTR(" ")); + PrintHex(l2capinbuf[12]); + Notify(PSTR(" SCID: ")); + PrintHex(l2capinbuf[15]); + Notify(PSTR(" ")); + PrintHex(l2capinbuf[14]); + Notify(PSTR(" Identifier: ")); + PrintHex(l2capinbuf[9]); +#endif + if ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) { identifier = l2capinbuf[9]; control_scid[0] = l2capinbuf[14]; control_scid[1] = l2capinbuf[15]; - l2cap_event_flag |= L2CAP_EV_CONTROL_CONNECTION_REQUEST; + l2cap_event_flag |= L2CAP_FLAG_CONNECTION_CONTROL_REQUEST; } - else if ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == L2CAP_PSM_HID_INTR) - { + else if ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_INTR_PSM) { identifier = l2capinbuf[9]; interrupt_scid[0] = l2capinbuf[14]; interrupt_scid[1] = l2capinbuf[15]; - l2cap_event_flag |= L2CAP_EV_INTERRUPT_CONNECTION_REQUEST; + l2cap_event_flag |= L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST; } } - else if (l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) - { - if (l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) - { - if ((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000)//Success - { + else if (l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) { + if (l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { + if ((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { //Success //Serial.print("\r\nHID Control Configuration Complete"); - l2cap_event_flag |= L2CAP_EV_CONTROL_CONFIG_SUCCESS; + l2cap_event_flag |= L2CAP_FLAG_CONFIG_CONTROL_SUCCESS; } } - else if (l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) - { - if ((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000)//Success - { + else if (l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) { + if ((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { //Success //Serial.print("\r\nHID Interrupt Configuration Complete"); - l2cap_event_flag |= L2CAP_EV_INTERRUPT_CONFIG_SUCCESS; + l2cap_event_flag |= L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS; } } } - else if (l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) - { - if (l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) - { + else if (l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) { + if (l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { //Serial.print("\r\nHID Control Configuration Request"); identifier = l2capinbuf[9]; - l2cap_event_flag |= L2CAP_EV_CONTROL_CONFIG_REQUEST; + l2cap_event_flag |= L2CAP_FLAG_CONFIG_CONTROL_REQUEST; } - else if (l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) - { + else if (l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) { //Serial.print("\r\nHID Interrupt Configuration Request"); identifier = l2capinbuf[9]; - l2cap_event_flag |= L2CAP_EV_INTERRUPT_CONFIG_REQUEST; + l2cap_event_flag |= L2CAP_FLAG_CONFIG_INTERRUPT_REQUEST; } } - else if (l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) - { - if (l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) - { + else if (l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) { + if (l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) { #ifdef DEBUG Notify(PSTR("\r\nDisconnect Request: Control Channel")); #endif identifier = l2capinbuf[9]; - l2cap_disconnection_response(identifier,control_dcid,control_scid); + pBtd->l2cap_disconnection_response(hci_handle,identifier,control_dcid,control_scid); + l2cap_event_flag = 0; // Reset flags + l2cap_state = L2CAP_EV_WAIT; } - else if (l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) - { + else if (l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) { #ifdef DEBUG Notify(PSTR("\r\nDisconnect Request: Interrupt Channel")); #endif identifier = l2capinbuf[9]; - l2cap_disconnection_response(identifier,interrupt_dcid,interrupt_scid); + pBtd->l2cap_disconnection_response(hci_handle,identifier,interrupt_dcid,interrupt_scid); + l2cap_event_flag = 0; // Reset flags + l2cap_state = L2CAP_EV_WAIT; } } - else if (l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) - { - if (l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) - { + else if (l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) { + if (l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) { //Serial.print("\r\nDisconnect Response: Control Channel"); identifier = l2capinbuf[9]; - l2cap_event_flag |= L2CAP_EV_CONTROL_DISCONNECT_RESPONSE; + l2cap_event_flag |= L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE; } - else if (l2capinbuf[12] == interrupt_scid[0] && l2capinbuf[13] == interrupt_scid[1]) - { + else if (l2capinbuf[12] == interrupt_scid[0] && l2capinbuf[13] == interrupt_scid[1]) { //Serial.print("\r\nDisconnect Response: Interrupt Channel"); identifier = l2capinbuf[9]; - l2cap_event_flag |= L2CAP_EV_INTERRUPT_DISCONNECT_RESPONSE; + l2cap_event_flag |= L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE; } - } - } - else if (l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1])//l2cap_interrupt - { - //Serial.print("\r\nL2CAP Interrupt"); - if(PS3Connected || PS3MoveConnected || PS3NavigationConnected) - { - readReport(); -#ifdef PRINTREPORT - printReport(); //Uncomment "#define PRINTREPORT" to print the report send by the PS3 Controllers -#endif } - } - L2CAP_task(); - } - } - else { #ifdef EXTRADEBUG - Notify(PSTR("\r\nACL data in error: ")); - PrintHex(rcode); + else { + Notify(PSTR("\r\nL2CAP Unknown Signaling Command: ")); + PrintHex(l2capinbuf[8]); + } #endif + } else if (l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1]) { // l2cap_interrupt + //Serial.print("\r\nL2CAP Interrupt"); + if(PS3Connected || PS3MoveConnected || PS3NavigationConnected) { + /* Read Report */ + if(l2capinbuf[8] == 0xA1) // HID_THDR_DATA_INPUT + { + if(PS3Connected || PS3NavigationConnected) + ButtonState = (uint32_t)(l2capinbuf[11] | ((uint16_t)l2capinbuf[12] << 8) | ((uint32_t)l2capinbuf[13] << 16)); + else if(PS3MoveConnected) + ButtonState = (uint32_t)(l2capinbuf[10] | ((uint16_t)l2capinbuf[11] << 8) | ((uint32_t)l2capinbuf[12] << 16)); + + //Notify(PSTR("\r\nButtonState"); + //PrintHex(ButtonState); + + if(ButtonState != OldButtonState) + { + buttonChanged = true; + if(ButtonState != 0x00) { + buttonPressed = true; + buttonReleased = false; + } else { + buttonPressed = false; + buttonReleased = true; + } + } + + else + { + buttonChanged = false; + buttonPressed = false; + buttonReleased = false; + } + + OldButtonState = ButtonState; + } +#ifdef PRINTREPORT // Uncomment "#define PRINTREPORT" to print the report send by the PS3 Controllers + if(l2capinbuf[8] == 0xA1) { //HID_THDR_DATA_INPUT + for(uint8_t i = 10; i < 58;i++) { + PrintHex(l2capinbuf[i]); + Serial.print(" "); + } + Serial.println(); + } +#endif + } + } } } -void PS3BT::L2CAP_task() -{ - switch (l2cap_state) - { +void PS3BT::Poll() { + switch (l2cap_state) { case L2CAP_EV_WAIT: - break; - case L2CAP_EV_CONTROL_SETUP: - if (l2cap_control_connection_request) - { + if (l2cap_connection_request_control_flag) { #ifdef DEBUG Notify(PSTR("\r\nHID Control Incoming Connection Request")); #endif - l2cap_connection_response(identifier, control_dcid, control_scid, PENDING); + pBtd->l2cap_connection_response(hci_handle,identifier, control_dcid, control_scid, PENDING); delay(1); - l2cap_connection_response(identifier, control_dcid, control_scid, SUCCESSFUL); + pBtd->l2cap_connection_response(hci_handle,identifier, control_dcid, control_scid, SUCCESSFUL); identifier++; delay(1); - l2cap_config_request(identifier, control_scid); + pBtd->l2cap_config_request(hci_handle,identifier, control_scid); l2cap_state = L2CAP_EV_CONTROL_REQUEST; } break; case L2CAP_EV_CONTROL_REQUEST: - if (l2cap_control_config_request) - { + if (l2cap_config_request_control_flag) { #ifdef DEBUG Notify(PSTR("\r\nHID Control Configuration Request")); #endif - l2cap_config_response(identifier, control_scid); + pBtd->l2cap_config_response(hci_handle,identifier, control_scid); l2cap_state = L2CAP_EV_CONTROL_SUCCESS; } break; case L2CAP_EV_CONTROL_SUCCESS: - if (l2cap_control_config_success) - { + if (l2cap_config_success_control_flag) { #ifdef DEBUG Notify(PSTR("\r\nHID Control Successfully Configured")); #endif @@ -1100,42 +450,39 @@ void PS3BT::L2CAP_task() } break; case L2CAP_EV_INTERRUPT_SETUP: - if (l2cap_interrupt_connection_request) - { + if (l2cap_connection_request_interrupt_flag) { #ifdef DEBUG Notify(PSTR("\r\nHID Interrupt Incoming Connection Request")); #endif - l2cap_connection_response(identifier, interrupt_dcid, interrupt_scid, PENDING); + pBtd->l2cap_connection_response(hci_handle,identifier, interrupt_dcid, interrupt_scid, PENDING); delay(1); - l2cap_connection_response(identifier, interrupt_dcid, interrupt_scid, SUCCESSFUL); + pBtd->l2cap_connection_response(hci_handle,identifier, interrupt_dcid, interrupt_scid, SUCCESSFUL); identifier++; delay(1); - l2cap_config_request(identifier, interrupt_scid); + pBtd->l2cap_config_request(hci_handle,identifier, interrupt_scid); l2cap_state = L2CAP_EV_INTERRUPT_REQUEST; } break; case L2CAP_EV_INTERRUPT_REQUEST: - if (l2cap_interrupt_config_request) - { + if (l2cap_config_request_interrupt_flag) { #ifdef DEBUG Notify(PSTR("\r\nHID Interrupt Configuration Request")); #endif - l2cap_config_response(identifier, interrupt_scid); + pBtd->l2cap_config_response(hci_handle,identifier, interrupt_scid); l2cap_state = L2CAP_EV_INTERRUPT_SUCCESS; } break; case L2CAP_EV_INTERRUPT_SUCCESS: - if (l2cap_interrupt_config_success) - { + if (l2cap_config_success_interrupt_flag) { #ifdef DEBUG Notify(PSTR("\r\nHID Interrupt Successfully Configured")); #endif if(remote_name[0] == 'M') { // First letter in Motion Controller ('M') for (uint8_t i = 0; i < BULK_MAXPKTSIZE; i++) // Reset l2cap in buffer as it sometimes read it as a button has been pressed - l2capinbuf[i] = 0; + l2capinpointer[i] = 0; ButtonState = 0; - OldButtonState = 0; + OldButtonState = 0; l2cap_state = L2CAP_EV_HID_PS3_LED; } else @@ -1146,13 +493,13 @@ void PS3BT::L2CAP_task() case L2CAP_EV_HID_ENABLE_SIXAXIS: if(millis() - timer > 1000) { // loop 1 second before sending the command for (uint8_t i = 0; i < BULK_MAXPKTSIZE; i++) // Reset l2cap in buffer as it sometimes read it as a button has been pressed - l2capinbuf[i] = 0; + l2capinpointer[i] = 0; ButtonState = 0; - OldButtonState = 0; + OldButtonState = 0; - enable_sixaxis(); + enable_sixaxis(); for (uint8_t i = 15; i < 19; i++) - l2capinbuf[i] = 0x7F; // Set the analog joystick values to center position + l2capinpointer[i] = 0x7F; // Set the analog joystick values to center position l2cap_state = L2CAP_EV_HID_PS3_LED; timer = millis(); } @@ -1180,11 +527,11 @@ void PS3BT::L2CAP_task() #endif PS3MoveConnected = true; } - l2cap_state = L2CAP_EV_L2CAP_DONE; - } + l2cap_state = L2CAP_EV_DONE; + } break; - case L2CAP_EV_L2CAP_DONE: + case L2CAP_EV_DONE: if (PS3MoveConnected)//The Bulb and rumble values, has to be send at aproximatly every 5th second for it to stay on { if (millis() - timerBulbRumble > 4000)//Send at least every 4th second @@ -1196,345 +543,47 @@ void PS3BT::L2CAP_task() break; case L2CAP_EV_INTERRUPT_DISCONNECT: - if (l2cap_interrupt_disconnect_response) - { + if (l2cap_disconnect_response_interrupt_flag) { #ifdef DEBUG Notify(PSTR("\r\nDisconnected Interrupt Channel")); #endif identifier++; - l2cap_disconnection_request(identifier, control_dcid, control_scid); + pBtd->l2cap_disconnection_request(hci_handle, identifier, control_scid, control_dcid); + l2cap_event_flag = 0; // Reset flags l2cap_state = L2CAP_EV_CONTROL_DISCONNECT; } break; case L2CAP_EV_CONTROL_DISCONNECT: - if (l2cap_control_disconnect_response) - { + if (l2cap_disconnect_response_control_flag) { #ifdef DEBUG Notify(PSTR("\r\nDisconnected Control Channel")); #endif - hci_disconnect(); - l2cap_state = L2CAP_EV_L2CAP_DONE; - hci_state = HCI_DISCONNECT_STATE; + pBtd->hci_disconnect(hci_handle); + l2cap_state = L2CAP_EV_WAIT; } break; } } -/************************************************************/ -/* HID Report (HCI ACL Packet) */ -/************************************************************/ -void PS3BT::readReport() -{ - if (l2capinbuf == NULL) - return; - if(l2capinbuf[8] == 0xA1)//HID_THDR_DATA_INPUT - { - if(PS3Connected || PS3NavigationConnected) - ButtonState = (uint32_t)(l2capinbuf[11] | ((uint16_t)l2capinbuf[12] << 8) | ((uint32_t)l2capinbuf[13] << 16)); - else if(PS3MoveConnected) - ButtonState = (uint32_t)(l2capinbuf[10] | ((uint16_t)l2capinbuf[11] << 8) | ((uint32_t)l2capinbuf[12] << 16)); - - //Notify(PSTR("\r\nButtonState"); - //PrintHex(ButtonState); - - if(ButtonState != OldButtonState) - { - buttonChanged = true; - if(ButtonState != 0x00) { - buttonPressed = true; - buttonReleased = false; - } else { - buttonPressed = false; - buttonReleased = true; - } - } - - else - { - buttonChanged = false; - buttonPressed = false; - buttonReleased = false; - } - - OldButtonState = ButtonState; - } -} - -void PS3BT::printReport() //Uncomment "#define PRINTREPORT" to print the report send by the PS3 Controllers -{ - if (l2capinbuf == NULL) - return; - if(l2capinbuf[8] == 0xA1)//HID_THDR_DATA_INPUT - { - for(uint8_t i = 10; i < 58;i++) - { - PrintHex(l2capinbuf[i]); - Serial.print(" "); - } - Serial.println(""); - } -} - -/************************************************************/ -/* HCI Commands */ -/************************************************************/ - -//perform HCI Command -void PS3BT::HCI_Command(uint8_t* data, uint16_t nbytes) -{ - hci_event_flag &= ~HCI_FLAG_CMD_COMPLETE; - pUsb->ctrlReq(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, bmREQ_HCI_OUT, 0x00, 0x00, 0x00 ,0x00, nbytes, nbytes, data, NULL); -} - -void PS3BT::hci_reset() -{ - hci_event_flag = 0; // clear all the flags - hcibuf[0] = 0x03; // HCI OCF = 3 - hcibuf[1] = 0x03 << 2; // HCI OGF = 3 - hcibuf[2] = 0x00; - HCI_Command(hcibuf, 3); -} -void PS3BT::hci_write_scan_enable() -{ - hci_event_flag &= ~HCI_FLAG_INCOMING_REQUEST; - hcibuf[0] = 0x1A; // HCI OCF = 1A - hcibuf[1] = 0x03 << 2; // HCI OGF = 3 - hcibuf[2] = 0x01;// parameter length = 1 - hcibuf[3] = 0x02;// Inquiry Scan disabled. Page Scan enabled. - HCI_Command(hcibuf, 4); -} -void PS3BT::hci_write_scan_disable() -{ - hcibuf[0] = 0x1A; // HCI OCF = 1A - hcibuf[1] = 0x03 << 2; // HCI OGF = 3 - hcibuf[2] = 0x01;// parameter length = 1 - hcibuf[3] = 0x00;// Inquiry Scan disabled. Page Scan disabled. - HCI_Command(hcibuf, 4); -} -void PS3BT::hci_read_bdaddr() -{ - hcibuf[0] = 0x09; // HCI OCF = 9 - hcibuf[1] = 0x04 << 2; // HCI OGF = 4 - hcibuf[2] = 0x00; - HCI_Command(hcibuf, 3); -} -void PS3BT::hci_read_local_version_information() -{ - hcibuf[0] = 0x01; // HCI OCF = 1 - hcibuf[1] = 0x04 << 2; // HCI OGF = 4 - hcibuf[2] = 0x00; - HCI_Command(hcibuf, 3); -} -void PS3BT::hci_accept_connection() -{ - hcibuf[0] = 0x09; // HCI OCF = 9 - hcibuf[1] = 0x01 << 2; // HCI OGF = 1 - hcibuf[2] = 0x07; // parameter length 7 - hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr - hcibuf[4] = disc_bdaddr[1]; - hcibuf[5] = disc_bdaddr[2]; - hcibuf[6] = disc_bdaddr[3]; - hcibuf[7] = disc_bdaddr[4]; - hcibuf[8] = disc_bdaddr[5]; - hcibuf[9] = 0; //switch role to master - - HCI_Command(hcibuf, 10); -} -void PS3BT::hci_remote_name() -{ - hci_event_flag &= ~HCI_FLAG_REMOTE_NAME_COMPLETE; - hcibuf[0] = 0x19; // HCI OCF = 19 - hcibuf[1] = 0x01 << 2; // HCI OGF = 1 - hcibuf[2] = 0x0A; // parameter length = 10 - hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr - hcibuf[4] = disc_bdaddr[1]; - hcibuf[5] = disc_bdaddr[2]; - hcibuf[6] = disc_bdaddr[3]; - hcibuf[7] = disc_bdaddr[4]; - hcibuf[8] = disc_bdaddr[5]; - hcibuf[9] = 0x01; //Page Scan Repetition Mode - hcibuf[10] = 0x00; //Reserved - hcibuf[11] = 0x00; //Clock offset - low byte - hcibuf[12] = 0x00; //Clock offset - high byte - - HCI_Command(hcibuf, 13); -} -void PS3BT::hci_disconnect() -{ - hci_event_flag &= ~HCI_FLAG_DISCONN_COMPLETE; - hcibuf[0] = 0x06; // HCI OCF = 6 - hcibuf[1]= 0x01 << 2; // HCI OGF = 1 - hcibuf[2] = 0x03; // parameter length =3 - hcibuf[3] = (uint8_t)(hci_handle & 0xFF);//connection handle - low byte - hcibuf[4] = (uint8_t)((hci_handle >> 8) & 0x0F);//connection handle - high byte - hcibuf[5] = 0x13; // reason - - HCI_Command(hcibuf, 6); -} -/************************************************************/ -/* L2CAP Commands */ -/************************************************************/ -void PS3BT::L2CAP_Command(uint8_t* data, uint16_t nbytes) -{ - uint8_t buf[64]; - buf[0] = (uint8_t)(hci_handle & 0xff); // HCI handle with PB,BC flag - buf[1] = (uint8_t)(((hci_handle >> 8) & 0x0f) | 0x20); - buf[2] = (uint8_t)((4 + nbytes) & 0xff); // HCI ACL total data length - buf[3] = (uint8_t)((4 + nbytes) >> 8); - buf[4] = (uint8_t)(nbytes & 0xff); // L2CAP header: Length - buf[5] = (uint8_t)(nbytes >> 8); - buf[6] = 0x01; // L2CAP header: Channel ID - buf[7] = 0x00; // L2CAP Signalling channel over ACL-U logical link - - for (uint16_t i = 0; i < nbytes; i++)//L2CAP C-frame - buf[8 + i] = data[i]; - - uint8_t rcode = pUsb->outTransfer(bAddress, epInfo[ BTD_DATAOUT_PIPE ].epAddr, (8 + nbytes), buf); - if(rcode) - { -#ifdef DEBUG - Notify(PSTR("\r\nError sending message: 0x")); - PrintHex(rcode); -#endif - } -} -void PS3BT::l2cap_connection_response(uint8_t rxid, uint8_t dcid[], uint8_t scid[], uint8_t result) -{ - l2capoutbuf[0] = L2CAP_CMD_CONNECTION_RESPONSE;// Code - l2capoutbuf[1] = rxid;// Identifier - l2capoutbuf[2] = 0x08;// Length - l2capoutbuf[3] = 0x00; - l2capoutbuf[4] = dcid[0];// Destination CID - l2capoutbuf[5] = dcid[1]; - l2capoutbuf[6] = scid[0];// Source CID - l2capoutbuf[7] = scid[1]; - l2capoutbuf[8] = result;// Result: Pending or Success - l2capoutbuf[9] = 0x00; - l2capoutbuf[10] = 0x00;// No further information - l2capoutbuf[11] = 0x00; - - L2CAP_Command(l2capoutbuf, 12); -} -void PS3BT::l2cap_config_request(uint8_t rxid, uint8_t dcid[]) -{ - l2capoutbuf[0] = L2CAP_CMD_CONFIG_REQUEST;// Code - l2capoutbuf[1] = rxid;// Identifier - l2capoutbuf[2] = 0x08;// Length - l2capoutbuf[3] = 0x00; - l2capoutbuf[4] = dcid[0];// Destination CID - l2capoutbuf[5] = dcid[1]; - l2capoutbuf[6] = 0x00;// Flags - l2capoutbuf[7] = 0x00; - l2capoutbuf[8] = 0x01;// Config Opt: type = MTU (Maximum Transmission Unit) - Hint - l2capoutbuf[9] = 0x02;// Config Opt: length - l2capoutbuf[10] = 0xFF;// MTU - l2capoutbuf[11] = 0xFF; - - L2CAP_Command(l2capoutbuf, 12); -} -void PS3BT::l2cap_config_response(uint8_t rxid, uint8_t scid[]) -{ - l2capoutbuf[0] = L2CAP_CMD_CONFIG_RESPONSE;// Code - l2capoutbuf[1] = rxid;// Identifier - l2capoutbuf[2] = 0x0A;// Length - l2capoutbuf[3] = 0x00; - l2capoutbuf[4] = scid[0];// Source CID - l2capoutbuf[5] = scid[1]; - l2capoutbuf[6] = 0x00;// Flag - l2capoutbuf[7] = 0x00; - l2capoutbuf[8] = 0x00;// Result - l2capoutbuf[9] = 0x00; - l2capoutbuf[10] = 0x01;// Config - l2capoutbuf[11] = 0x02; - l2capoutbuf[12] = 0xA0; - l2capoutbuf[13] = 0x02; - - L2CAP_Command(l2capoutbuf, 14); -} -void PS3BT::l2cap_disconnection_request(uint8_t rxid, uint8_t dcid[], uint8_t scid[]) -{ - l2capoutbuf[0] = L2CAP_CMD_DISCONNECT_REQUEST;// Code - l2capoutbuf[1] = rxid;// Identifier - l2capoutbuf[2] = 0x04;// Length - l2capoutbuf[3] = 0x00; - l2capoutbuf[4] = scid[0];// Really Destination CID - l2capoutbuf[5] = scid[1]; - l2capoutbuf[6] = dcid[0];// Really Source CID - l2capoutbuf[7] = dcid[1]; - L2CAP_Command(l2capoutbuf, 8); -} -void PS3BT::l2cap_disconnection_response(uint8_t rxid, uint8_t dcid[], uint8_t scid[]) -{ - l2capoutbuf[0] = L2CAP_CMD_DISCONNECT_RESPONSE;// Code - l2capoutbuf[1] = rxid;// Identifier - l2capoutbuf[2] = 0x04;// Length - l2capoutbuf[3] = 0x00; - l2capoutbuf[4] = scid[0];// Really Destination CID - l2capoutbuf[5] = scid[1]; - l2capoutbuf[6] = dcid[0];// Really Source CID - l2capoutbuf[7] = dcid[1]; - L2CAP_Command(l2capoutbuf, 8); -} -/******************************************************************* - * * - * HCI ACL Data Packet * - * * - * buf[0] buf[1] buf[2] buf[3] - * 0 4 8 11 12 16 24 31 MSB - * .-+-+-+-+-+-+-+-|-+-+-+-|-+-|-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. - * | HCI Handle |PB |BC | Data Total Length | HCI ACL Data Packet - * .-+-+-+-+-+-+-+-|-+-+-+-|-+-|-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. - * - * buf[4] buf[5] buf[6] buf[7] - * 0 8 16 31 MSB - * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. - * | Length | Channel ID | Basic L2CAP header - * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. - * - * buf[8] buf[9] buf[10] buf[11] - * 0 8 16 31 MSB - * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. - * | Code | Identifier | Length | Control frame (C-frame) - * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. (signaling packet format) - */ - /************************************************************/ /* HID Commands */ /************************************************************/ //Playstation Sixaxis Dualshock and Navigation Controller commands -void PS3BT::HID_Command(uint8_t* data, uint16_t nbytes) -{ - uint8_t buf[64]; - buf[0] = (uint8_t)(hci_handle & 0xff); // HCI handle with PB,BC flag - buf[1] = (uint8_t)(((hci_handle >> 8) & 0x0f) | 0x20); - buf[2] = (uint8_t)((4 + nbytes) & 0xff); // HCI ACL total data length - buf[3] = (uint8_t)((4 + nbytes) >> 8); - buf[4] = (uint8_t)(nbytes & 0xff); // L2CAP header: Length - buf[5] = (uint8_t)(nbytes >> 8); - buf[6] = control_scid[0];//Both the Navigation and Dualshock controller sends data via the controller channel - buf[7] = control_scid[1]; - - for (uint16_t i = 0; i < nbytes; i++)//L2CAP C-frame - buf[8 + i] = data[i]; - +void PS3BT::HID_Command(uint8_t* data, uint8_t nbytes) { if (millis() - timerHID <= 250)// Check if is has been more than 250ms since last command delay((uint32_t)(250 - (millis() - timerHID)));//There have to be a delay between commands - - pUsb->outTransfer(bAddress, epInfo[ BTD_DATAOUT_PIPE ].epAddr, (8 + nbytes), buf); - + pBtd->L2CAP_Command(hci_handle,data,nbytes,control_scid[0],control_scid[1]); // Both the Navigation and Dualshock controller sends data via the controller channel timerHID = millis(); } -void PS3BT::setAllOff() -{ +void PS3BT::setAllOff() { for (uint8_t i = 0; i < OUTPUT_REPORT_BUFFER_SIZE; i++) HIDBuffer[i + 2] = pgm_read_byte(&OUTPUT_REPORT_BUFFER[i]);//First two bytes reserved for report type and ID HID_Command(HIDBuffer, HID_BUFFERSIZE); } -void PS3BT::setRumbleOff() -{ +void PS3BT::setRumbleOff() { HIDBuffer[3] = 0x00; HIDBuffer[4] = 0x00;//low mode off HIDBuffer[5] = 0x00; @@ -1542,8 +591,7 @@ void PS3BT::setRumbleOff() HID_Command(HIDBuffer, HID_BUFFERSIZE); } -void PS3BT::setRumbleOn(Rumble mode) -{ +void PS3BT::setRumbleOn(Rumble mode) { /* Still not totally sure how it works, maybe something like this instead? * 3 - duration_right * 4 - power_right @@ -1569,23 +617,19 @@ void PS3BT::setRumbleOn(Rumble mode) HID_Command(HIDBuffer, HID_BUFFERSIZE); } } -void PS3BT::setLedOff(LED a) -{ +void PS3BT::setLedOff(LED a) { HIDBuffer[11] &= ~((uint8_t)(((uint16_t)a & 0x0f) << 1)); HID_Command(HIDBuffer, HID_BUFFERSIZE); } -void PS3BT::setLedOn(LED a) -{ +void PS3BT::setLedOn(LED a) { HIDBuffer[11] |= (uint8_t)(((uint16_t)a & 0x0f) << 1); HID_Command(HIDBuffer, HID_BUFFERSIZE); } -void PS3BT::setLedToggle(LED a) -{ +void PS3BT::setLedToggle(LED a) { HIDBuffer[11] ^= (uint8_t)(((uint16_t)a & 0x0f) << 1); HID_Command(HIDBuffer, HID_BUFFERSIZE); } -void PS3BT::enable_sixaxis()//Command used to enable the Dualshock 3 and Navigation controller to send data via USB -{ +void PS3BT::enable_sixaxis() { //Command used to enable the Dualshock 3 and Navigation controller to send data via USB uint8_t cmd_buf[6]; cmd_buf[0] = 0x53;// HID BT Set_report (0x50) | Report Type (Feature 0x03) cmd_buf[1] = 0xF4;// Report ID @@ -1598,30 +642,13 @@ void PS3BT::enable_sixaxis()//Command used to enable the Dualshock 3 and Navigat } //Playstation Move Controller commands -void PS3BT::HIDMove_Command(uint8_t* data,uint16_t nbytes) -{ - uint8_t buf[64]; - buf[0] = (uint8_t)(hci_handle & 0xff); // HCI handle with PB,BC flag - buf[1] = (uint8_t)(((hci_handle >> 8) & 0x0f) | 0x20); - buf[2] = (uint8_t)((4 + nbytes) & 0xff); // HCI ACL total data length - buf[3] = (uint8_t)((4 + nbytes) >> 8); - buf[4] = (uint8_t)(nbytes & 0xff); // L2CAP header: Length - buf[5] = (uint8_t)(nbytes >> 8); - buf[6] = interrupt_scid[0];//The Move controller sends it's data via the intterrupt channel - buf[7] = interrupt_scid[1]; - - for (uint16_t i = 0; i < nbytes; i++)//L2CAP C-frame - buf[8 + i] = data[i]; - - if (millis() - timerHID <= 250)// Check if is has been less than 200ms since last command +void PS3BT::HIDMove_Command(uint8_t* data,uint8_t nbytes) { + if (millis() - timerHID <= 250)// Check if is has been less than 200ms since last command delay((uint32_t)(250 - (millis() - timerHID)));//There have to be a delay between commands - - pUsb->outTransfer(bAddress, epInfo[ BTD_DATAOUT_PIPE ].epAddr, (8 + nbytes), buf); - + pBtd->L2CAP_Command(hci_handle,data,nbytes,interrupt_scid[0],interrupt_scid[1]); // The Move controller sends it's data via the intterrupt channel timerHID = millis(); } -void PS3BT::moveSetBulb(uint8_t r, uint8_t g, uint8_t b)//Use this to set the Color using RGB values -{ +void PS3BT::moveSetBulb(uint8_t r, uint8_t g, uint8_t b) { //Use this to set the Color using RGB values //set the Bulb's values into the write buffer HIDMoveBuffer[3] = r; HIDMoveBuffer[4] = g; @@ -1629,12 +656,10 @@ void PS3BT::moveSetBulb(uint8_t r, uint8_t g, uint8_t b)//Use this to set the Co HIDMove_Command(HIDMoveBuffer, HID_BUFFERSIZE); } -void PS3BT::moveSetBulb(Colors color)//Use this to set the Color using the predefined colors in "enums.h" -{ +void PS3BT::moveSetBulb(Colors color) { //Use this to set the Color using the predefined colors in enum moveSetBulb((uint8_t)(color >> 16),(uint8_t)(color >> 8),(uint8_t)(color)); } -void PS3BT::moveSetRumble(uint8_t rumble) -{ +void PS3BT::moveSetRumble(uint8_t rumble) { #ifdef DEBUG if(rumble < 64 && rumble != 0) // The rumble value has to at least 64, or approximately 25% (64/255*100) Notify(PSTR("\r\nThe rumble value has to at least 64, or approximately 25%")); diff --git a/PS3BT.h b/PS3BT.h index ae038dad..88128150 100644 --- a/PS3BT.h +++ b/PS3BT.h @@ -18,153 +18,49 @@ #ifndef _ps3bt_h_ #define _ps3bt_h_ -#if defined(ARDUINO) && ARDUINO >= 100 -#include "Arduino.h" -#else -#include "WProgram.h" -#endif - -#include "Usb.h" -#include "confdescparser.h" - -/* CSR Bluetooth data taken from descriptors */ -#define INT_MAXPKTSIZE 16 // max size for HCI data -#define BULK_MAXPKTSIZE 64 // max size for ACL data - -/* PS3 data taken from descriptors */ -#define EP_MAXPKTSIZE 64 // max size for data via USB - -/* Endpoint types */ -#define EP_INTERRUPT 0x03 - -/* Names we give to the 3 ps3 pipes - this is only used for setting the bluetooth address into the ps3 controllers */ -#define PS3_CONTROL_PIPE 0 -#define PS3_OUTPUT_PIPE 1 -#define PS3_INPUT_PIPE 2 - -//PID and VID of the different devices -#define PS3_VID 0x054C // Sony Corporation -#define PS3_PID 0x0268 // PS3 Controller DualShock 3 -#define PS3NAVIGATION_PID 0x042F // Navigation controller -#define PS3MOVE_PID 0x03D5 // Motion controller +#include "BTD.h" #define HID_BUFFERSIZE 50 // size of the buffer for the Playstation Motion Controller #define OUTPUT_REPORT_BUFFER_SIZE 48 //Size of the output report buffer for the controllers -// used in control endpoint header for HCI Commands -#define bmREQ_HCI_OUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE - -// used in control endpoint header for HID Commands -#define bmREQ_HID_OUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE -#define HID_REQUEST_SET_REPORT 0x09 - -/* Bluetooth HCI states for hci_task() */ -#define HCI_INIT_STATE 0 -#define HCI_RESET_STATE 1 -#define HCI_BDADDR_STATE 2 -#define HCI_LOCAL_VERSION_STATE 3 -#define HCI_SCANNING_STATE 4 -#define HCI_CONNECT_IN_STATE 5 -#define HCI_REMOTE_NAME_STATE 6 -#define HCI_CONNECTED_STATE 7 -#define HCI_DISABLE_SCAN 8 -#define HCI_DONE_STATE 9 -#define HCI_DISCONNECT_STATE 10 - -/* HCI event flags*/ -#define HCI_FLAG_CMD_COMPLETE 0x01 -#define HCI_FLAG_CONN_COMPLETE 0x02 -#define HCI_FLAG_DISCONN_COMPLETE 0x04 -#define HCI_FLAG_REMOTE_NAME_COMPLETE 0x08 -#define HCI_FLAG_INCOMING_REQUEST 0x10 -#define HCI_FLAG_READ_BDADDR 0x20 -#define HCI_FLAG_READ_VERSION 0x40 - -/*Macros for HCI event flag tests */ -#define hci_cmd_complete (hci_event_flag & HCI_FLAG_CMD_COMPLETE) -#define hci_connect_complete (hci_event_flag & HCI_FLAG_CONN_COMPLETE) -#define hci_disconnect_complete (hci_event_flag & HCI_FLAG_DISCONN_COMPLETE) -#define hci_remote_name_complete (hci_event_flag & HCI_FLAG_REMOTE_NAME_COMPLETE) -#define hci_incoming_connect_request (hci_event_flag & HCI_FLAG_INCOMING_REQUEST) -#define hci_read_bdaddr_complete (hci_event_flag & HCI_FLAG_READ_BDADDR) -#define hci_read_version_complete (hci_event_flag & HCI_FLAG_READ_VERSION) - -/* HCI Events managed */ -#define EV_COMMAND_COMPLETE 0x0E -#define EV_COMMAND_STATUS 0x0F -#define EV_CONNECT_COMPLETE 0x03 -#define EV_DISCONNECT_COMPLETE 0x05 -#define EV_NUM_COMPLETE_PKT 0x13 -#define EV_INQUIRY_COMPLETE 0x01 -#define EV_INQUIRY_RESULT 0x02 -#define EV_REMOTE_NAME_COMPLETE 0x07 -#define EV_INCOMING_CONNECT 0x04 -#define EV_ROLE_CHANGED 0x12 -#define EV_PAGE_SCAN_REP_MODE 0x20 -#define EV_DATA_BUFFER_OVERFLOW 0x1A -#define EV_LOOPBACK_COMMAND 0x19 -#define EV_CHANGE_CONNECTION_LINK 0x09 -#define EV_AUTHENTICATION_COMPLETE 0x06 - /* Bluetooth L2CAP states for L2CAP_task() */ #define L2CAP_EV_WAIT 0 -#define L2CAP_EV_CONTROL_SETUP 1 -#define L2CAP_EV_CONTROL_REQUEST 2 -#define L2CAP_EV_CONTROL_SUCCESS 3 -#define L2CAP_EV_INTERRUPT_SETUP 4 -#define L2CAP_EV_INTERRUPT_REQUEST 5 -#define L2CAP_EV_INTERRUPT_SUCCESS 6 -#define L2CAP_EV_HID_ENABLE_SIXAXIS 7 -#define L2CAP_EV_HID_PS3_LED 8 -#define L2CAP_EV_L2CAP_DONE 9 -#define L2CAP_EV_INTERRUPT_DISCONNECT 10 -#define L2CAP_EV_CONTROL_DISCONNECT 11 +#define L2CAP_EV_CONTROL_REQUEST 1 +#define L2CAP_EV_CONTROL_SUCCESS 2 +#define L2CAP_EV_INTERRUPT_SETUP 3 +#define L2CAP_EV_INTERRUPT_REQUEST 4 +#define L2CAP_EV_INTERRUPT_SUCCESS 5 +#define L2CAP_EV_HID_ENABLE_SIXAXIS 6 +#define L2CAP_EV_HID_PS3_LED 7 +#define L2CAP_EV_DONE 8 +#define L2CAP_EV_INTERRUPT_DISCONNECT 9 +#define L2CAP_EV_CONTROL_DISCONNECT 10 /* L2CAP event flags */ -#define L2CAP_EV_CONTROL_CONNECTION_REQUEST 0x01 -#define L2CAP_EV_CONTROL_CONFIG_REQUEST 0x02 -#define L2CAP_EV_CONTROL_CONFIG_SUCCESS 0x04 -#define L2CAP_EV_INTERRUPT_CONNECTION_REQUEST 0x08 -#define L2CAP_EV_INTERRUPT_CONFIG_REQUEST 0x10 -#define L2CAP_EV_INTERRUPT_CONFIG_SUCCESS 0x20 -#define L2CAP_EV_CONTROL_DISCONNECT_RESPONSE 0x40 -#define L2CAP_EV_INTERRUPT_DISCONNECT_RESPONSE 0x80 +#define L2CAP_FLAG_CONNECTION_CONTROL_REQUEST 0x01 +#define L2CAP_FLAG_CONFIG_CONTROL_REQUEST 0x02 +#define L2CAP_FLAG_CONFIG_CONTROL_SUCCESS 0x04 +#define L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST 0x08 +#define L2CAP_FLAG_CONFIG_INTERRUPT_REQUEST 0x10 +#define L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS 0x20 +#define L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE 0x40 +#define L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE 0x80 /*Macros for L2CAP event flag tests */ -#define l2cap_control_connection_request (l2cap_event_flag & L2CAP_EV_CONTROL_CONNECTION_REQUEST) -#define l2cap_control_config_request (l2cap_event_flag & L2CAP_EV_CONTROL_CONFIG_REQUEST) -#define l2cap_control_config_success (l2cap_event_flag & L2CAP_EV_CONTROL_CONFIG_SUCCESS) -#define l2cap_interrupt_connection_request (l2cap_event_flag & L2CAP_EV_INTERRUPT_CONNECTION_REQUEST) -#define l2cap_interrupt_config_request (l2cap_event_flag & L2CAP_EV_INTERRUPT_CONFIG_REQUEST) -#define l2cap_interrupt_config_success (l2cap_event_flag & L2CAP_EV_INTERRUPT_CONFIG_SUCCESS) -#define l2cap_control_disconnect_response (l2cap_event_flag & L2CAP_EV_CONTROL_DISCONNECT_RESPONSE) -#define l2cap_interrupt_disconnect_response (l2cap_event_flag & L2CAP_EV_INTERRUPT_DISCONNECT_RESPONSE) - -/* L2CAP signaling commands */ -#define L2CAP_CMD_COMMAND_REJECT 0x01 -#define L2CAP_CMD_CONNECTION_REQUEST 0x02 -#define L2CAP_CMD_CONNECTION_RESPONSE 0x03 -#define L2CAP_CMD_CONFIG_REQUEST 0x04 -#define L2CAP_CMD_CONFIG_RESPONSE 0x05 -#define L2CAP_CMD_DISCONNECT_REQUEST 0x06 -#define L2CAP_CMD_DISCONNECT_RESPONSE 0x07 +#define l2cap_connection_request_control_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_CONTROL_REQUEST) +#define l2cap_config_request_control_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_CONTROL_REQUEST) +#define l2cap_config_success_control_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_CONTROL_SUCCESS) +#define l2cap_connection_request_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST) +#define l2cap_config_request_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_INTERRUPT_REQUEST) +#define l2cap_config_success_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS) +#define l2cap_disconnect_response_control_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE) +#define l2cap_disconnect_response_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE) /* Bluetooth L2CAP PSM */ -#define L2CAP_PSM_HID_CTRL 0x11 // HID_Control -#define L2CAP_PSM_HID_INTR 0x13 // HID_Interrupt +#define HID_CTRL_PSM 0x11 // HID_Control +#define HID_INTR_PSM 0x13 // HID_Interrupt -// Used For Connection Response - Remember to Include High Byte -#define PENDING 0x01 -#define SUCCESSFUL 0x00 - -// Used to determine if it is a Bluetooth dongle -#define WI_SUBCLASS_RF 0x01 -#define WI_PROTOCOL_BT 0x01 - -#define PS3_MAX_ENDPOINTS 4 - -enum LED -{ +enum LED { LED1 = 0x01, LED2 = 0x02, LED3 = 0x04, @@ -177,8 +73,7 @@ enum LED LED9 = 0x0E, LED10 = 0x0F, }; -enum Colors -{ +enum Colors { // Used to set the colors of the move controller Red = 0xFF0000, // r = 255, g = 0, b = 0 Green = 0xFF00, // r = 0, g = 255, b = 0 @@ -192,8 +87,7 @@ enum Colors Off = 0x00, // r = 0, g = 0, b = 0 }; -enum Button -{ +enum Button { // byte location | bit location // Sixaxis Dualshcock 3 & Navigation controller @@ -237,8 +131,7 @@ enum Button T_MOVE = (12 << 8) | 0x10, // covers 12 bits - we only need to read the top 8 */ }; -enum AnalogButton -{ +enum AnalogButton { //Sixaxis Dualshcock 3 & Navigation controller UP_ANALOG = 23, RIGHT_ANALOG = 24, @@ -257,15 +150,13 @@ enum AnalogButton //Playstation Move Controller T_ANALOG = 15, // Both at byte 14 (last reading) and byte 15 (current reading) }; -enum AnalogHat -{ +enum AnalogHat { LeftHatX = 15, LeftHatY = 16, RightHatX = 17, RightHatY = 18, }; -enum Sensor -{ +enum Sensor { //Sensors inside the Sixaxis Dualshock 3 controller aX = 50, aY = 52, @@ -287,13 +178,11 @@ enum Sensor mZmove = 49, mYmove = 50, }; -enum Angle -{ +enum Angle { Pitch = 0x01, Roll = 0x02, }; -enum Status -{ +enum Status { // byte location | bit location Plugged = (38 << 8) | 0x02, Unplugged = (38 << 8) | 0x03, @@ -319,32 +208,21 @@ enum Status BluetoothRumble = (40 << 8) | 0x14,//Opperating by bluetooth and rumble is turned on Bluetooth = (40 << 8) | 0x16,//Opperating by bluetooth and rumble is turned off }; -enum Rumble -{ +enum Rumble { RumbleHigh = 0x10, RumbleLow = 0x20, }; -class PS3BT : public USBDeviceConfig, public UsbConfigXtracter -{ +class PS3BT : public BluetoothService { public: - PS3BT(USB *pUsb, uint8_t btadr5=0, uint8_t btadr4=0, uint8_t btadr3=0, uint8_t btadr2=0, uint8_t btadr1=0, uint8_t btadr0=0); - - // USBDeviceConfig implementation - virtual uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); - virtual uint8_t Release(); - virtual uint8_t Poll(); - virtual uint8_t GetAddress() { return bAddress; }; - virtual bool isReady() { return bPollEnable; }; - - // UsbConfigXtracter implementation - virtual void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep); - - bool isWatingForConnection() { return watingForConnection; }; // Use this to indicate when it is ready for a incoming connection - - void setBdaddr(uint8_t* BDADDR); - void setMoveBdaddr(uint8_t* BDADDR); + PS3BT(BTD *pBtd, uint8_t btadr5=0, uint8_t btadr4=0, uint8_t btadr3=0, uint8_t btadr2=0, uint8_t btadr1=0, uint8_t btadr0=0); + // BluetoothService implementation + virtual void ACLData(uint8_t* ACLData); // Used to pass acldata to the services + virtual void Poll(); // Used to run the state maschine + virtual void Release(); + virtual void disconnect(); // Use this void to disconnect any of the controllers + /* PS3 Controller Commands */ bool getButton(Button b); uint8_t getAnalogButton(AnalogButton a); @@ -354,7 +232,6 @@ public: bool getStatus(Status c); String getStatusString(); String getTemperature(); - void disconnect(); // use this void to disconnect any of the controllers /* HID Commands */ /* Commands for Dualshock 3 and Navigation controller */ @@ -364,6 +241,7 @@ public: void setLedOff(LED a); void setLedOn(LED a); void setLedToggle(LED a); + /* Commands for Motion controller only */ void moveSetBulb(uint8_t r, uint8_t g, uint8_t b);//Use this to set the Color using RGB values void moveSetBulb(Colors color);//Use this to set the Color using the predefined colors in "enum Colors" @@ -376,39 +254,13 @@ public: bool buttonPressed;//Indicate if a button has been pressed bool buttonReleased;//Indicate if a button has been released -protected: +private: /* mandatory members */ - USB *pUsb; - uint8_t bAddress; // device address - EpInfo epInfo[PS3_MAX_ENDPOINTS]; //endpoint info structure + BTD *pBtd; - uint8_t bConfNum; // configuration number - uint8_t bNumEP; // total number of endpoints in the configuration - uint32_t qNextPollTime; // next poll time - - #define BTD_CONTROL_PIPE 0 // Bluetooth dongles control endpoint - static const uint8_t BTD_EVENT_PIPE; // HCI event endpoint index - static const uint8_t BTD_DATAIN_PIPE; // ACL In endpoint index - static const uint8_t BTD_DATAOUT_PIPE; // ACL Out endpoint index - - void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr); - -private: - bool bPollEnable; - uint8_t pollInterval; - bool watingForConnection; - - /*variables filled from HCI event management */ + /* Variables filled from HCI event management */ int16_t hci_handle; - uint8_t disc_bdaddr[6]; // the bluetooth address is always 6 bytes - uint8_t remote_name[30]; // first 30 chars of remote name - uint8_t hci_version; - - /* variables used by high level HCI task */ - uint8_t hci_state; //current state of bluetooth hci connection - uint16_t hci_counter; // counter used for bluetooth hci reset loops - uint8_t hci_num_reset_loops; // this value indicate how many times it should read before trying to reset - uint16_t hci_event_flag;// hci flags of received bluetooth events + uint8_t remote_name[30]; // first 30 chars of remote name /* variables used by high level L2CAP task */ uint8_t l2cap_state; @@ -421,50 +273,21 @@ private: uint32_t timerHID;// timer used see if there has to be a delay before a new HID command uint32_t timerBulbRumble;// used to continuously set PS3 Move controller Bulb and rumble values - uint8_t my_bdaddr[6]; // Change to your dongles Bluetooth address in the constructor - uint8_t hcibuf[BULK_MAXPKTSIZE];//General purpose buffer for hci data - uint8_t l2capinbuf[BULK_MAXPKTSIZE];//General purpose buffer for l2cap in data uint8_t l2capoutbuf[BULK_MAXPKTSIZE];//General purpose buffer for l2cap out data + uint8_t* l2capinpointer; // Pointer to l2capinbuf from BTD uint8_t HIDBuffer[HID_BUFFERSIZE];// Used to store HID commands uint8_t HIDMoveBuffer[HID_BUFFERSIZE];// Used to store HID commands for the Move controller /* L2CAP Channels */ uint8_t control_scid[2];// L2CAP source CID for HID_Control - uint8_t control_dcid[2];//0x0040 + uint8_t control_dcid[2];//0x0040 uint8_t interrupt_scid[2];// L2CAP source CID for HID_Interrupt uint8_t interrupt_dcid[2];//0x0041 - uint8_t identifier;//Identifier for connection - - void HCI_event_task(); //poll the HCI event pipe - void HCI_task(); // HCI state machine - void ACL_event_task(); // start polling the ACL input pipe too, though discard data until connected - void L2CAP_task(); // L2CAP state machine - - void readReport(); // read incoming data - void printReport(); // print incoming date - Uncomment for debugging - - /* HCI Commands */ - void HCI_Command(uint8_t* data, uint16_t nbytes); - void hci_reset(); - void hci_write_scan_enable(); - void hci_write_scan_disable(); - void hci_read_bdaddr(); - void hci_read_local_version_information(); - void hci_accept_connection(); - void hci_remote_name(); - void hci_disconnect(); - - /* L2CAP Commands */ - void L2CAP_Command(uint8_t* data, uint16_t nbytes); - void l2cap_connection_response(uint8_t rxid, uint8_t dcid[], uint8_t scid[], uint8_t result); - void l2cap_config_request(uint8_t rxid, uint8_t dcid[]); - void l2cap_config_response(uint8_t rxid, uint8_t scid[]); - void l2cap_disconnection_request(uint8_t rxid, uint8_t dcid[], uint8_t scid[]); - void l2cap_disconnection_response(uint8_t rxid, uint8_t dcid[], uint8_t scid[]); + uint8_t identifier;//Identifier for connection /* HID Commands */ - void HID_Command(uint8_t* data, uint16_t nbytes); - void HIDMove_Command(uint8_t* data, uint16_t nbytes); + void HID_Command(uint8_t* data, uint8_t nbytes); + void HIDMove_Command(uint8_t* data, uint8_t nbytes); void enable_sixaxis();//Command used to enable the Dualshock 3 and Navigation controller to send data via USB }; #endif \ No newline at end of file diff --git a/RFCOMM.cpp b/RFCOMM.cpp deleted file mode 100644 index 177ef665..00000000 --- a/RFCOMM.cpp +++ /dev/null @@ -1,1594 +0,0 @@ -/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. - - This software may be distributed and modified under the terms of the GNU - General Public License version 2 (GPL2) as published by the Free Software - Foundation and appearing in the file GPL2.TXT included in the packaging of - this file. Please note that GPL2 Section 2[b] requires that all works based - on this software must also be made publicly available under the terms of - the GPL2 ("Copyleft"). - - Contact information - ------------------- - - Kristian Lauszus, TKJ Electronics - Web : http://www.tkjelectronics.com - e-mail : kristianl@tkjelectronics.com - */ - -#include "RFCOMM.h" -#define DEBUG // Uncomment to print data for debugging -//#define EXTRADEBUG // Uncomment to get even more debugging data -//#define PRINTREPORT // Uncomment to print the report sent to the Arduino - -const uint8_t RFCOMM::BTD_EVENT_PIPE = 1; -const uint8_t RFCOMM::BTD_DATAIN_PIPE = 2; -const uint8_t RFCOMM::BTD_DATAOUT_PIPE = 3; - -/* - * CRC (reversed crc) lookup table as calculated by the table generator in ETSI TS 101 369 V6.3.0. - */ -const uint8_t rfcomm_crc_table[256] PROGMEM = { /* reversed, 8-bit, poly=0x07 */ - 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B, - 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67, - 0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43, - 0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F, - 0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B, - 0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17, - 0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33, - 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F, - 0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B, - 0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87, - 0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3, - 0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF, - 0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB, - 0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7, - 0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3, - 0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF -}; - -RFCOMM::RFCOMM(USB *p, const char* name, const char* pin): -pUsb(p), // pointer to USB class instance - mandatory -bAddress(0), // device address - mandatory -bNumEP(1), // if config descriptor needs to be parsed -qNextPollTime(0), -bPollEnable(false) // don't start polling before dongle is connected -{ - for(uint8_t i=0; iRegisterDeviceClass(this); //set devConfig[] entry - - btdName = name; - btdPin = pin; -} - -uint8_t RFCOMM::Init(uint8_t parent, uint8_t port, bool lowspeed) -{ - uint8_t buf[sizeof(USB_DEVICE_DESCRIPTOR)]; - uint8_t rcode; - UsbDevice *p = NULL; - EpInfo *oldep_ptr = NULL; - uint8_t num_of_conf; // number of configurations - - // get memory address of USB device address pool - AddressPool &addrPool = pUsb->GetAddressPool(); -#ifdef EXTRADEBUG - Notify(PSTR("\r\nRFCOMM Init")); -#endif - // check if address has already been assigned to an instance - if (bAddress) - { -#ifdef DEBUG - Notify(PSTR("\r\nAddress in use")); -#endif - return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE; - } - - // Get pointer to pseudo device with address 0 assigned - p = addrPool.GetUsbDevicePtr(0); - - if (!p) - { -#ifdef DEBUG - Notify(PSTR("\r\nAddress not found")); -#endif - return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; - } - - if (!p->epinfo) - { -#ifdef DEBUG - Notify(PSTR("\r\nepinfo is null")); -#endif - return USB_ERROR_EPINFO_IS_NULL; - } - - // Save old pointer to EP_RECORD of address 0 - oldep_ptr = p->epinfo; - - // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence - p->epinfo = epInfo; - - p->lowspeed = lowspeed; - - // Get device descriptor - rcode = pUsb->getDevDescr(0, 0, sizeof(USB_DEVICE_DESCRIPTOR), (uint8_t*)buf);// Get device descriptor - addr, ep, nbytes, data - - // Restore p->epinfo - p->epinfo = oldep_ptr; - - if(rcode) - goto FailGetDevDescr; - - // Allocate new address according to device class - bAddress = addrPool.AllocAddress(parent, false, port); - - if (!bAddress) - return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL; - - // Extract Max Packet Size from device descriptor - epInfo[0].maxPktSize = (uint8_t)((USB_DEVICE_DESCRIPTOR*)buf)->bMaxPacketSize0; - - // Assign new address to the device - rcode = pUsb->setAddr( 0, 0, bAddress ); - if (rcode) - { - p->lowspeed = false; - addrPool.FreeAddress(bAddress); - bAddress = 0; -#ifdef DEBUG - Notify(PSTR("\r\nsetAddr: ")); -#endif - PrintHex(rcode); - return rcode; - } -#ifdef EXTRADEBUG - Notify(PSTR("\r\nAddr: ")); - PrintHex(bAddress); -#endif - p->lowspeed = false; - - //get pointer to assigned address record - p = addrPool.GetUsbDevicePtr(bAddress); - if (!p) - return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL; - - p->lowspeed = lowspeed; - - // Assign epInfo to epinfo pointer - only EP0 is known - rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); - if (rcode) - goto FailSetDevTblEntry; - - num_of_conf = ((USB_DEVICE_DESCRIPTOR*)buf)->bNumConfigurations; - - // check if attached device is a Bluetooth dongle and fill endpoint data structure - // first interface in the configuration must have Bluetooth assigned Class/Subclass/Protocol - // and 3 endpoints - interrupt-IN, bulk-IN, bulk-OUT, - // not necessarily in this order - for (uint8_t i=0; i confDescrParser(this); - rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser); - if(rcode) - goto FailGetConfDescr; - if(bNumEP > 3) //all endpoints extracted - break; - } - - if (bNumEP < BTD_MAX_ENDPOINTS) - goto FailUnknownDevice; - - // Assign epInfo to epinfo pointer - this time all 3 endpoins - rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo); - if(rcode) - goto FailSetDevTblEntry; - - delay(200); // Give time for address change - - // Set Configuration Value - rcode = pUsb->setConf(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, bConfNum); - if(rcode) - goto FailSetConf; - - /* Set device cid for the SDP and RFCOMM channelse */ - sdp_dcid[0] = 0x50;//0x0050 - sdp_dcid[1] = 0x00; - rfcomm_dcid[0] = 0x51;//0x0051 - rfcomm_dcid[1] = 0x00; - - hci_num_reset_loops = 100; // only loop 100 times before trying to send the hci reset command - - hci_state = HCI_INIT_STATE; - hci_counter = 0; - l2cap_sdp_state = L2CAP_SDP_WAIT; - l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT; -#ifdef DEBUG - Notify(PSTR("\r\nBluetooth Dongle Initialized")); -#endif - - watingForConnection = false; - bPollEnable = true; - - return 0; //successful configuration - - /* diagnostic messages */ -FailGetDevDescr: -#ifdef DEBUG - Notify(PSTR("\r\ngetDevDescr:")); -#endif - goto Fail; -FailSetDevTblEntry: -#ifdef DEBUG - Notify(PSTR("\r\nsetDevTblEn:")); -#endif - goto Fail; -FailGetConfDescr: -#ifdef DEBUG - Notify(PSTR("\r\ngetConf:")); -#endif - goto Fail; -FailSetConf: -#ifdef DEBUG - Notify(PSTR("\r\nsetConf:")); -#endif - goto Fail; -FailUnknownDevice: -#ifdef DEBUG - Notify(PSTR("\r\nUnknown Device Connected:")); -#endif - goto Fail; -Fail: -#ifdef DEBUG - Notify(PSTR("\r\nBTD Init Failed, error code: ")); - Serial.print(rcode); -#endif - Release(); - return rcode; -} -/* Extracts interrupt-IN, bulk-IN, bulk-OUT endpoint information from config descriptor */ -void RFCOMM::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) { - //ErrorMessage(PSTR("Conf.Val"),conf); - //ErrorMessage(PSTR("Iface Num"),iface); - //ErrorMessage(PSTR("Alt.Set"),alt); - - if(alt) // wrong interface - by BT spec, no alt setting - return; - - bConfNum = conf; - uint8_t index; - - if ((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80) //Interrupt In endpoint found - index = BTD_EVENT_PIPE; - - else { - if ((pep->bmAttributes & 0x02) == 2) //bulk endpoint found - index = ((pep->bEndpointAddress & 0x80) == 0x80) ? BTD_DATAIN_PIPE : BTD_DATAOUT_PIPE; - else - return; - } - - //Fill the rest of endpoint data structure - epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F); - epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize; -#ifdef EXTRADEBUG - PrintEndpointDescriptor(pep); -#endif - if(pollInterval < pep->bInterval) // Set the polling interval as the largest polling interval obtained from endpoints - pollInterval = pep->bInterval; - bNumEP++; - return; -} -void RFCOMM::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) { - Notify(PSTR("\r\nEndpoint descriptor:")); - Notify(PSTR("\r\nLength:\t\t")); - PrintHex(ep_ptr->bLength); - Notify(PSTR("\r\nType:\t\t")); - PrintHex(ep_ptr->bDescriptorType); - Notify(PSTR("\r\nAddress:\t")); - PrintHex(ep_ptr->bEndpointAddress); - Notify(PSTR("\r\nAttributes:\t")); - PrintHex(ep_ptr->bmAttributes); - Notify(PSTR("\r\nMaxPktSize:\t")); - PrintHex(ep_ptr->wMaxPacketSize); - Notify(PSTR("\r\nPoll Intrv:\t")); - PrintHex(ep_ptr->bInterval); -} - -/* Performs a cleanup after failed Init() attempt */ -uint8_t RFCOMM::Release() { - connected = false; - pUsb->GetAddressPool().FreeAddress(bAddress); - bAddress = 0; - bPollEnable = false; - bNumEP = 1; // must have to be reset to 1 - return 0; -} -uint8_t RFCOMM::Poll() { - if (!bPollEnable) - return 0; - if (qNextPollTime <= millis()) { // Don't poll if shorter than polling interval - qNextPollTime = millis() + pollInterval; // Set new poll time - HCI_event_task(); // poll the HCI event pipe - ACL_event_task(); // start polling the ACL input pipe too, though discard data until connected - } - return 0; -} - -void RFCOMM::disconnect() { // Use this void to disconnect the RFCOMM Channel - connected = false; - // First the two L2CAP channels has to be disconencted and then the HCI connection - if(RFCOMMConnected) - l2cap_disconnection_request(0x0A, rfcomm_dcid, rfcomm_scid); - if(SDPConnected) - l2cap_disconnection_request(0x0B, sdp_dcid, sdp_scid); - l2cap_sdp_state = L2CAP_DISCONNECT_RESPONSE; -} - -void RFCOMM::HCI_event_task() { - /* check the event pipe*/ - uint16_t MAX_BUFFER_SIZE = BULK_MAXPKTSIZE; // Request more than 16 bytes anyway, the inTransfer routine will take care of this - uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ BTD_EVENT_PIPE ].epAddr, &MAX_BUFFER_SIZE, hcibuf); // input on endpoint 1 - if(!rcode || rcode == hrNAK) // Check for errors - { - switch (hcibuf[0]) //switch on event type - { - case EV_COMMAND_COMPLETE: - hci_event_flag |= HCI_FLAG_CMD_COMPLETE; // set command complete flag - if (!hcibuf[5]) { // check if command succeeded - if((hcibuf[3] == 0x09) && (hcibuf[4] == 0x10)) { // parameters from read local bluetooth address - for (uint8_t i = 0; i < 6; i++) - my_bdaddr[i] = hcibuf[6 + i]; - hci_event_flag |= HCI_FLAG_READ_BDADDR; - } - } - break; - - case EV_COMMAND_STATUS: - if(hcibuf[2]) // show status on serial if not OK - { -#ifdef DEBUG - Notify(PSTR("\r\nHCI Command Failed: ")); - PrintHex(hcibuf[2]); - Notify(PSTR(" ")); - PrintHex(hcibuf[4]); - Notify(PSTR(" ")); - PrintHex(hcibuf[5]); -#endif - } - break; - - case EV_CONNECT_COMPLETE: - if (!hcibuf[2]) { // check if connected OK - hci_handle = hcibuf[3] | hcibuf[4] << 8; //store the handle for the ACL connection - hci_event_flag |= HCI_FLAG_CONN_COMPLETE; // set connection complete flag - } - break; - - case EV_DISCONNECT_COMPLETE: - if (!hcibuf[2]) { // check if disconnected OK - hci_event_flag |= HCI_FLAG_DISCONN_COMPLETE; //set disconnect commend complete flag - hci_event_flag &= ~HCI_FLAG_CONN_COMPLETE; // clear connection complete flag - } - break; - - case EV_REMOTE_NAME_COMPLETE: - if (!hcibuf[2]) { // check if reading is OK - for (uint8_t i = 0; i < 30; i++) - remote_name[i] = hcibuf[9 + i]; //store first 30 bytes - hci_event_flag |= HCI_FLAG_REMOTE_NAME_COMPLETE; - } - break; - - case EV_INCOMING_CONNECT: - disc_bdaddr[0] = hcibuf[2]; - disc_bdaddr[1] = hcibuf[3]; - disc_bdaddr[2] = hcibuf[4]; - disc_bdaddr[3] = hcibuf[5]; - disc_bdaddr[4] = hcibuf[6]; - disc_bdaddr[5] = hcibuf[7]; - hci_event_flag |= HCI_FLAG_INCOMING_REQUEST; - break; - - case EV_PIN_CODE_REQUEST: -#ifdef DEBUG - Notify(PSTR("\r\nBluetooth pin is set too: ")); - Serial.print(btdPin); -#endif - hci_pin_code_request_reply(btdPin); - break; - - case EV_LINK_KEY_REQUEST: -#ifdef DEBUG - Notify(PSTR("\r\nReceived Key Request")); -#endif - hci_link_key_request_negative_reply(); - break; - - /* We will just ignore the following events */ - case EV_NUM_COMPLETE_PKT: - case EV_ROLE_CHANGED: - case EV_PAGE_SCAN_REP_MODE: - case EV_LOOPBACK_COMMAND: - case EV_DATA_BUFFER_OVERFLOW: - case EV_CHANGE_CONNECTION_LINK: - case EV_AUTHENTICATION_COMPLETE: - case EV_MAX_SLOTS_CHANGE: - case EV_QOS_SETUP_COMPLETE: - case EV_LINK_KEY_NOTIFICATION: - case EV_ENCRYPTION_CHANGE: - case EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE: - break; - - default: -#ifdef EXTRADEBUG - if(hcibuf[0] != 0x00) { - Notify(PSTR("\r\nUnmanaged HCI Event: ")); - PrintHex(hcibuf[0]); - } -#endif - break; - } // switch - HCI_task(); - } - else { -#ifdef EXTRADEBUG - Notify(PSTR("\r\nHCI event error: ")); - PrintHex(rcode); -#endif - } -} - -/* Poll Bluetooth and print result */ -void RFCOMM::HCI_task() { - switch (hci_state){ - case HCI_INIT_STATE: - hci_counter++; - if (hci_counter > hci_num_reset_loops) // wait until we have looped x times to clear any old events - { - hci_reset(); - hci_state = HCI_RESET_STATE; - hci_counter = 0; - } - break; - - case HCI_RESET_STATE: - hci_counter++; - if (hci_cmd_complete) - { -#ifdef DEBUG - Notify(PSTR("\r\nHCI Reset complete")); -#endif - hci_state = HCI_BDADDR_STATE; - hci_read_bdaddr(); - } - else if (hci_counter > hci_num_reset_loops) - { - hci_num_reset_loops *= 10; - if(hci_num_reset_loops > 2000) - hci_num_reset_loops = 2000; -#ifdef DEBUG - Notify(PSTR("\r\nNo response to HCI Reset")); -#endif - hci_state = HCI_INIT_STATE; - hci_counter = 0; - } - break; - - case HCI_BDADDR_STATE: - if (hci_read_bdaddr_complete) - { -#ifdef DEBUG - Notify(PSTR("\r\nLocal Bluetooth Address: ")); - for(int8_t i = 5; i > 0;i--) - { - PrintHex(my_bdaddr[i]); - Serial.print(":"); - } - PrintHex(my_bdaddr[0]); -#endif - hci_set_local_name(btdName); - hci_state = HCI_SET_NAME_STATE; - } - break; - - case HCI_SET_NAME_STATE: - if (hci_cmd_complete) { -#ifdef DEBUG - Notify(PSTR("\r\nThe name is set to: ")); - Serial.print(btdName); -#endif - hci_state = HCI_SCANNING_STATE; - } - break; - case HCI_SCANNING_STATE: -#ifdef DEBUG - Notify(PSTR("\r\nWait For Incoming Connection Request")); -#endif - hci_write_scan_enable(); - watingForConnection = true; - hci_state = HCI_CONNECT_IN_STATE; - break; - - case HCI_CONNECT_IN_STATE: - if(hci_incoming_connect_request) { - watingForConnection = false; -#ifdef DEBUG - Notify(PSTR("\r\nIncoming Request")); -#endif - hci_remote_name(); - hci_state = HCI_REMOTE_NAME_STATE; - } - break; - - case HCI_REMOTE_NAME_STATE: - if(hci_remote_name_complete) { -#ifdef DEBUG - Notify(PSTR("\r\nRemote Name: ")); - for (uint8_t i = 0; i < 30; i++) - { - if(remote_name[i] == NULL) - break; - Serial.write(remote_name[i]); - } -#endif - hci_accept_connection(); - hci_state = HCI_CONNECTED_STATE; - } - break; - - case HCI_CONNECTED_STATE: - if (hci_connect_complete) { -#ifdef DEBUG - Notify(PSTR("\r\nConnected to Device: ")); - for(int8_t i = 5; i>0;i--) - { - PrintHex(disc_bdaddr[i]); - Serial.print(":"); - } - PrintHex(disc_bdaddr[0]); -#endif - hci_write_scan_disable(); // Only allow one connection - hci_state = HCI_DISABLE_SCAN; - } - break; - - case HCI_DISABLE_SCAN: - if (hci_cmd_complete) { -#ifdef DEBUG - Notify(PSTR("\r\nScan Disabled")); -#endif - l2cap_event_flag = 0; - hci_state = HCI_DONE_STATE; - } - break; - - case HCI_DONE_STATE: - if (hci_disconnect_complete) - hci_state = HCI_DISCONNECT_STATE; - break; - - case HCI_DISCONNECT_STATE: - if (hci_disconnect_complete) { -#ifdef DEBUG - Notify(PSTR("\r\nDisconnected from Device: ")); - for(int8_t i = 5; i>0;i--) - { - PrintHex(disc_bdaddr[i]); - Serial.print(":"); - } - PrintHex(disc_bdaddr[0]); -#endif - l2cap_event_flag = 0; // Clear all flags - hci_event_flag = 0; // Clear all flags - - //Reset all buffers - for (uint8_t i = 0; i < BULK_MAXPKTSIZE; i++) - hcibuf[i] = 0; - for (uint8_t i = 0; i < BULK_MAXPKTSIZE; i++) - l2capinbuf[i] = 0; - for (uint8_t i = 0; i < BULK_MAXPKTSIZE; i++) - l2capoutbuf[i] = 0; - for (uint8_t i = 0; i < BULK_MAXPKTSIZE; i++) - rfcommbuf[i] = 0; - - l2cap_sdp_state = L2CAP_SDP_WAIT; - l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT; - hci_state = HCI_SCANNING_STATE; - } - break; - default: - break; - } -} - -void RFCOMM::ACL_event_task() -{ - uint16_t MAX_BUFFER_SIZE = BULK_MAXPKTSIZE; - uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ BTD_DATAIN_PIPE ].epAddr, &MAX_BUFFER_SIZE, l2capinbuf); // input on endpoint 2 - if(!rcode) { // Check for errors - if (((l2capinbuf[0] | (l2capinbuf[1] << 8)) == (hci_handle | 0x2000))) { //acl_handle_ok - if ((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001) { //l2cap_control - Channel ID for ACL-U - /* - if (l2capinbuf[8] != 0x00) - { - Serial.print("\r\nL2CAP Signaling Command - 0x"); - PrintHex(l2capoutbuf[8]); - } - */ - if (l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) { -#ifdef DEBUG - Notify(PSTR("\r\nL2CAP Command Rejected - Reason: ")); - PrintHex(l2capinbuf[13]); - Notify(PSTR(" ")); - PrintHex(l2capinbuf[12]); - Notify(PSTR(" Data: ")); - PrintHex(l2capinbuf[17]); - Notify(PSTR(" ")); - PrintHex(l2capinbuf[16]); - Notify(PSTR(" ")); - PrintHex(l2capinbuf[15]); - Notify(PSTR(" ")); - PrintHex(l2capinbuf[14]); -#endif - } - else if (l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) { -#ifdef EXTRADEBUG - Notify(PSTR("\r\nL2CAP Connection Request - PSM: ")); - PrintHex(l2capinbuf[13]); - Notify(PSTR(" ")); - PrintHex(l2capinbuf[12]); - Notify(PSTR(" ")); - - Notify(PSTR(" SCID: ")); - PrintHex(l2capinbuf[15]); - Notify(PSTR(" ")); - PrintHex(l2capinbuf[14]); - - Notify(PSTR(" Identifier: ")); - PrintHex(l2capinbuf[9]); -#endif - if ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == SDP_PSM) { - identifier = l2capinbuf[9]; - sdp_scid[0] = l2capinbuf[14]; - sdp_scid[1] = l2capinbuf[15]; - l2cap_event_flag |= L2CAP_FLAG_CONNECTION_SDP_REQUEST; - - } - if ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == RFCOMM_PSM) { - identifier = l2capinbuf[9]; - rfcomm_scid[0] = l2capinbuf[14]; - rfcomm_scid[1] = l2capinbuf[15]; - l2cap_event_flag |= L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST; - - } - } - else if (l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) { - if (l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) { - if ((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { //Success - //Serial.print("\r\nSDP Configuration Complete"); - l2cap_event_flag |= L2CAP_FLAG_CONFIG_SDP_SUCCESS; - } - } - else if (l2capinbuf[12] == rfcomm_dcid[0] && l2capinbuf[13] == rfcomm_dcid[1]) { - if ((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { //Success - //Serial.print("\r\nRFCOMM Configuration Complete"); - l2cap_event_flag |= L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS; - } - } - } - else if (l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) { - if (l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) { - //Serial.print("\r\nSDP Configuration Request"); - identifier = l2capinbuf[9]; - l2cap_event_flag |= L2CAP_FLAG_CONFIG_SDP_REQUEST; - } - else if (l2capinbuf[12] == rfcomm_dcid[0] && l2capinbuf[13] == rfcomm_dcid[1]) { - //Serial.print("\r\nRFCOMM Configuration Request"); - identifier = l2capinbuf[9]; - l2cap_event_flag |= L2CAP_FLAG_CONFIG_RFCOMM_REQUEST; - } - } - else if (l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) { - if (l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) { - //Notify(PSTR("\r\nDisconnect Request: SDP Channel")); - identifier = l2capinbuf[9]; - l2cap_event_flag |= L2CAP_FLAG_DISCONNECT_SDP_REQUEST; - } - else if (l2capinbuf[12] == rfcomm_dcid[0] && l2capinbuf[13] == rfcomm_dcid[1]) { - //Notify(PSTR("\r\nDisconnect Request: RFCOMM Channel")); - identifier = l2capinbuf[9]; - l2cap_event_flag |= L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST; - } - } - else if (l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) { - if (l2capinbuf[12] == sdp_scid[0] && l2capinbuf[13] == sdp_scid[1]) { - //Serial.print("\r\nDisconnect Response: SDP Channel"); - identifier = l2capinbuf[9]; - l2cap_event_flag |= L2CAP_FLAG_DISCONNECT_RESPONSE; - } - else if (l2capinbuf[12] == rfcomm_scid[0] && l2capinbuf[13] == rfcomm_scid[1]) { - //Serial.print("\r\nDisconnect Response: RFCOMM Channel"); - identifier = l2capinbuf[9]; - l2cap_event_flag |= L2CAP_FLAG_DISCONNECT_RESPONSE; - } - } - else if (l2capinbuf[8] == L2CAP_CMD_INFORMATION_REQUEST) { -#ifdef DEBUG - Notify(PSTR("\r\nInformation request")); -#endif - identifier = l2capinbuf[9]; - l2cap_information_response(identifier,l2capinbuf[12],l2capinbuf[13]); - } - else { -#ifdef EXTRADEBUG - Notify(PSTR("\r\nL2CAP Unknown Command: ")); -#endif - PrintHex(l2capinbuf[8]); - } - } - else if (l2capinbuf[6] == sdp_dcid[0] && l2capinbuf[7] == sdp_dcid[1]) { // SDP - if(l2capinbuf[8] == SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU) { - /* - Serial.print("\r\nUUID: 0x"); - Serial.print(l2capinbuf[16],HEX); - Serial.print(" "); - Serial.print(l2capinbuf[17],HEX); - */ - if ((l2capinbuf[16] << 8 | l2capinbuf[17]) == SERIALPORT_UUID) { - if(firstMessage) { - serialPortResponse1(l2capinbuf[9],l2capinbuf[10]); - firstMessage = false; - } else { - serialPortResponse2(l2capinbuf[9],l2capinbuf[10]); // Serialport continuation state - firstMessage = true; - } - } else if ((l2capinbuf[16] << 8 | l2capinbuf[17]) == L2CAP_UUID) { - if(firstMessage) { - l2capResponse1(l2capinbuf[9],l2capinbuf[10]); - firstMessage = false; - } else { - l2capResponse2(l2capinbuf[9],l2capinbuf[10]); // L2CAP continuation state - firstMessage = true; - } - } else - serviceNotSupported(l2capinbuf[9],l2capinbuf[10]); // The service is not supported - } - } - else if (l2capinbuf[6] == rfcomm_dcid[0] && l2capinbuf[7] == rfcomm_dcid[1]) { // RFCOMM - rfcommChannel = l2capinbuf[8] & 0xF8; - rfcommDirection = l2capinbuf[8] & 0x04; - rfcommCommandResponse = l2capinbuf[8] & 0x02; - rfcommChannelType = l2capinbuf[9] & 0xEF; - rfcommPfBit = l2capinbuf[9] & 0x10; - - if(rfcommChannel>>3 != 0x00) - rfcommChannelPermanent = rfcommChannel; - -#ifdef EXTRADEBUG - Notify(PSTR("\r\nRFCOMM Channel: ")); - Serial.print(rfcommChannel>>3,HEX); - Notify(PSTR(" Direction: ")); - Serial.print(rfcommDirection>>2,HEX); - Notify(PSTR(" CommandResponse: ")); - Serial.print(rfcommCommandResponse>>1,HEX); - Notify(PSTR(" ChannelType: ")); - Serial.print(rfcommChannelType,HEX); - Notify(PSTR(" PF_BIT: ")); - Serial.print(rfcommPfBit,HEX); -#endif - - if (rfcommChannelType == RFCOMM_DISC) { -#ifdef DEBUG - Notify(PSTR("\r\nReceived Disconnect RFCOMM Command on channel: ")); - Serial.print(rfcommChannel>>3,HEX); -#endif - connected = false; - sendRfcomm(rfcommChannel,rfcommDirection,rfcommCommandResponse,RFCOMM_UA,rfcommPfBit,rfcommbuf,0x00); // UA Command - } - if(connected) { - readReport(); -#ifdef PRINTREPORT - printReport(); //Uncomment "#define PRINTREPORT" to print the report send to the Arduino via Bluetooth -#endif - } else { - if(rfcommChannelType == RFCOMM_SABM) { // SABM Command - this is sent twice: once for channel 0 and then for the channel to establish -#ifdef DEBUG - Notify(PSTR("\r\nReceived SABM Command")); -#endif - sendRfcomm(rfcommChannel,rfcommDirection,rfcommCommandResponse,RFCOMM_UA,rfcommPfBit,rfcommbuf,0x00); // UA Command - } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_PN_CMD) { // UIH Parameter Negotiation Command -#ifdef DEBUG - Notify(PSTR("\r\nReceived UIH Parameter Negotiation Command")); -#endif - rfcommbuf[0] = BT_RFCOMM_PN_RSP; // UIH Parameter Negotiation Response - rfcommbuf[1] = l2capinbuf[12]; // Length and shiftet like so: length << 1 | 1 - rfcommbuf[2] = l2capinbuf[13]; // Channel: channel << 1 | 1 - rfcommbuf[3] = 0xE0; // Pre difined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM - rfcommbuf[4] = 0x00; // Priority - rfcommbuf[5] = 0x00; // Timer - rfcommbuf[6] = 0x40; // Max Fram Size LSB - we will just set this to 64 - rfcommbuf[7] = 0x00; // Max Fram Size MSB - rfcommbuf[8] = 0x00; // MaxRatransm. - rfcommbuf[9] = 0x00; // Number of Frames - sendRfcomm(rfcommChannel,rfcommDirection,0,RFCOMM_UIH,rfcommPfBit,rfcommbuf,0x0A); - } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_MSC_CMD) { // UIH Modem Status Command -#ifdef DEBUG - Notify(PSTR("\r\nSend UIH Modem Status Response")); -#endif - rfcommbuf[0] = BT_RFCOMM_MSC_RSP; // UIH Modem Status Response - rfcommbuf[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1 - rfcommbuf[2] = l2capinbuf[13]; // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3) - rfcommbuf[3] = l2capinbuf[14]; - sendRfcomm(rfcommChannel,rfcommDirection,0,RFCOMM_UIH,rfcommPfBit,rfcommbuf,0x04); - - delay(1); -#ifdef DEBUG - Notify(PSTR("\r\nSend UIH Modem Status Command")); -#endif - rfcommbuf[0] = BT_RFCOMM_MSC_CMD; // UIH Modem Status Command - rfcommbuf[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1 - rfcommbuf[2] = l2capinbuf[13]; // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3) - rfcommbuf[3] = 0x8D; // Can receive frames (YES), Ready to Communicate (YES), Ready to Receive (YES), Incomig Call (NO), Data is Value (YES) - - sendRfcomm(rfcommChannel,rfcommDirection,0,RFCOMM_UIH,rfcommPfBit,rfcommbuf,0x04); - } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_MSC_RSP) { // UIH Modem Status Response - if(!creditSent) { -#ifdef DEBUG - Notify(PSTR("\r\nSend UIH Command with credit")); -#endif - sendRfcommCredit(rfcommChannelPermanent,rfcommDirection,0,RFCOMM_UIH,0x10,0xFF); // 255 credit - creditSent = true; - timer = millis(); - waitForLastCommand = true; - } - } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[10] == 0x01) { // UIH Command with credit -#ifdef DEBUG - Notify(PSTR("\r\nReceived UIH Command with credit")); -#endif - } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_RPN_CMD) { // UIH Remote Port Negotiation Command -#ifdef DEBUG - Notify(PSTR("\r\nReceived UIH Remote Port Negotiation Command")); -#endif - rfcommbuf[0] = BT_RFCOMM_RPN_RSP; // Command - rfcommbuf[1] = l2capinbuf[12]; // Length and shiftet like so: length << 1 | 1 - rfcommbuf[2] = l2capinbuf[13]; // Channel: channel << 1 | 1 - rfcommbuf[3] = l2capinbuf[14]; // Pre difined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM - rfcommbuf[4] = l2capinbuf[15]; // Priority - rfcommbuf[5] = l2capinbuf[16]; // Timer - rfcommbuf[6] = l2capinbuf[17]; // Max Fram Size LSB - rfcommbuf[7] = l2capinbuf[18]; // Max Fram Size MSB - rfcommbuf[8] = l2capinbuf[19]; // MaxRatransm. - rfcommbuf[9] = l2capinbuf[20]; // Number of Frames - sendRfcomm(rfcommChannel,rfcommDirection,0,RFCOMM_UIH,rfcommPfBit,rfcommbuf,0x0A); // UIH Remote Port Negotiation Response -#ifdef DEBUG - Notify(PSTR("\r\nRFCOMM Connection is now established\r\n")); -#endif - waitForLastCommand = false; - creditSent = false; - connected = true; // The RFCOMM channel is now established - } else if(rfcommChannelType != RFCOMM_DISC) { -#ifdef DEBUG - Notify(PSTR("\r\nUnsupported RFCOMM Data - ChannelType: ")); - PrintHex(rfcommChannelType); - Notify(PSTR(" Command: ")); - PrintHex(l2capinbuf[11]); -#endif - } - } - } else { -#ifdef EXTRADEBUG - Notify(PSTR("\r\nUnsupported L2CAP Data - Channel ID: ")); - PrintHex(l2capinbuf[7]); - Notify(PSTR(" ")); - PrintHex(l2capinbuf[6]); -#endif - } - SDP_task(); - RFCOMM_task(); - } - } - else if (rcode != hrNAK) { -#ifdef EXTRADEBUG - Notify(PSTR("\r\nACL data in error: ")); - PrintHex(rcode); -#endif - } - if((millis() - timer) > 100 && waitForLastCommand) { // We will only wait 100ms and see if the UIH Remote Port Negotiation Command is send, as some deviced don't send it -#ifdef DEBUG - Notify(PSTR("\r\nRFCOMM Connection is now established - Automatic\r\n")); -#endif - creditSent = false; - waitForLastCommand = false; - connected = true; // The RFCOMM channel is now established - } -} -void RFCOMM::SDP_task() { - switch (l2cap_sdp_state) - { - case L2CAP_SDP_WAIT: - if (l2cap_connection_request_sdp_flag) { -#ifdef DEBUG - Notify(PSTR("\r\nSDP Incoming Connection Request")); -#endif - l2cap_connection_response(identifier, sdp_dcid, sdp_scid, PENDING); - delay(1); - l2cap_connection_response(identifier, sdp_dcid, sdp_scid, SUCCESSFUL); - identifier++; - delay(1); - l2cap_config_request(identifier, sdp_scid); - l2cap_sdp_state = L2CAP_SDP_REQUEST; - } - break; - case L2CAP_SDP_REQUEST: - if (l2cap_config_request_sdp_flag) { -#ifdef DEBUG - Notify(PSTR("\r\nSDP Configuration Request")); -#endif - l2cap_config_response(identifier, sdp_scid); - l2cap_sdp_state = L2CAP_SDP_SUCCESS; - } - break; - case L2CAP_SDP_SUCCESS: - if (l2cap_config_success_sdp_flag) { -#ifdef DEBUG - Notify(PSTR("\r\nSDP Successfully Configured")); -#endif - firstMessage = true; // Reset bool - SDPConnected = true; - l2cap_sdp_state = L2CAP_SDP_DONE; - } - break; - case L2CAP_SDP_DONE: - if(l2cap_disconnect_request_sdp_flag) { - SDPConnected = false; -#ifdef DEBUG - Notify(PSTR("\r\nDisconnected SDP Channel")); -#endif - l2cap_disconnection_response(identifier,sdp_dcid,sdp_scid); - l2cap_sdp_state = L2CAP_SDP_WAIT; - } - break; - case L2CAP_DISCONNECT_RESPONSE: // This is for both disconnection response from the RFCOMM and SDP channel if they were connected - if (l2cap_disconnect_response_flag) { -#ifdef DEBUG - Notify(PSTR("\r\nDisconnected L2CAP Connection")); -#endif - RFCOMMConnected = false; - SDPConnected = false; - hci_disconnect(); - l2cap_sdp_state = L2CAP_SDP_WAIT; - l2cap_sdp_state = L2CAP_RFCOMM_WAIT; - hci_state = HCI_DISCONNECT_STATE; - } - break; - } -} -void RFCOMM::RFCOMM_task() -{ - switch (l2cap_rfcomm_state) - { - case L2CAP_RFCOMM_WAIT: - if(l2cap_connection_request_rfcomm_flag) { -#ifdef DEBUG - Notify(PSTR("\r\nRFCOMM Incoming Connection Request")); -#endif - l2cap_connection_response(identifier, rfcomm_dcid, rfcomm_scid, PENDING); - delay(1); - l2cap_connection_response(identifier, rfcomm_dcid, rfcomm_scid, SUCCESSFUL); - identifier++; - delay(1); - l2cap_config_request(identifier, rfcomm_scid); - l2cap_rfcomm_state = L2CAP_RFCOMM_REQUEST; - } - break; - case L2CAP_RFCOMM_REQUEST: - if (l2cap_config_request_rfcomm_flag) { -#ifdef DEBUG - Notify(PSTR("\r\nRFCOMM Configuration Request")); -#endif - l2cap_config_response(identifier, rfcomm_scid); - l2cap_rfcomm_state = L2CAP_RFCOMM_SUCCESS; - } - break; - case L2CAP_RFCOMM_SUCCESS: - if (l2cap_config_success_rfcomm_flag) { -#ifdef DEBUG - Notify(PSTR("\r\nRFCOMM Successfully Configured")); -#endif - rfcommAvailable = 0; // Reset number of bytes available - bytesReceived = 0; // Reset number of bytes received - RFCOMMConnected = true; - l2cap_rfcomm_state = L2CAP_RFCOMM_DONE; - } - break; - case L2CAP_RFCOMM_DONE: - if(l2cap_disconnect_request_rfcomm_flag) { - RFCOMMConnected = false; - connected = false; -#ifdef DEBUG - Notify(PSTR("\r\nDisconnected RFCOMM Channel")); -#endif - l2cap_disconnection_response(identifier,rfcomm_dcid,rfcomm_scid); - l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT; - } - break; - } -} - -/************************************************************/ -/* RFCOMM Report */ -/************************************************************/ -void RFCOMM::readReport() { - if(rfcommChannelType != RFCOMM_UIH || rfcommChannel != rfcommChannelPermanent) - return; - uint8_t length = l2capinbuf[10] >> 1; // Get length - if(rfcommAvailable + length > 256) - return; // Return if the buffer would be full - - uint8_t offset = l2capinbuf[4]-length-4; // See if there is credit - - for(uint8_t i = 0; i < length; i++) - rfcommDataBuffer[rfcommAvailable+i] = l2capinbuf[11+i+offset]; - rfcommAvailable += length; - bytesReceived += length; - if(bytesReceived > 200) { - bytesReceived = 0; - sendRfcommCredit(rfcommChannelPermanent,rfcommDirection,0,RFCOMM_UIH,0x10,0xFF); // Send 255 more credit -#ifdef EXTRADEBUG - Notify(PSTR("\r\nSent more credit")); -#endif - } - -#ifdef EXTRADEBUG - Notify(PSTR("\r\nRFCOMM Data Available: ")); - Serial.print(rfcommAvailable); - if (offset) { - Notify(PSTR(" - Credit: 0x")); - Serial.print(l2capinbuf[11],HEX); - } -#endif -} -void RFCOMM::printReport() { //Uncomment "#define PRINTREPORT" to print the report send to the Arduino - if(rfcommChannelType != RFCOMM_UIH || rfcommChannel != rfcommChannelPermanent) - return; - uint8_t length = l2capinbuf[10] >> 1; // Get length - uint8_t offset = l2capinbuf[4]-length-4; // See if there is credit - for(uint8_t i = 0; i < length; i++) - Serial.write(l2capinbuf[i+11+offset]); -} - -/************************************************************/ -/* HCI Commands */ -/************************************************************/ -void RFCOMM::HCI_Command(uint8_t* data, uint16_t nbytes) { - hci_event_flag &= ~HCI_FLAG_CMD_COMPLETE; - pUsb->ctrlReq(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, bmREQ_HCI_OUT, 0x00, 0x00, 0x00 ,0x00, nbytes, nbytes, data, NULL); -} -void RFCOMM::hci_reset() { - hci_event_flag = 0; // clear all the flags - hcibuf[0] = 0x03; // HCI OCF = 3 - hcibuf[1] = 0x03 << 2; // HCI OGF = 3 - hcibuf[2] = 0x00; - HCI_Command(hcibuf, 3); -} -void RFCOMM::hci_write_scan_enable() { - hci_event_flag &= ~HCI_FLAG_INCOMING_REQUEST; - hcibuf[0] = 0x1A; // HCI OCF = 1A - hcibuf[1] = 0x03 << 2; // HCI OGF = 3 - hcibuf[2] = 0x01; // parameter length = 1 - hcibuf[3] = 0x03; // Inquiry Scan enabled. Page Scan enabled. - HCI_Command(hcibuf, 4); -} -void RFCOMM::hci_write_scan_disable() { - hcibuf[0] = 0x1A; // HCI OCF = 1A - hcibuf[1] = 0x03 << 2; // HCI OGF = 3 - hcibuf[2] = 0x01; // parameter length = 1 - hcibuf[3] = 0x00; // Inquiry Scan disabled. Page Scan disabled. - HCI_Command(hcibuf, 4); -} -void RFCOMM::hci_read_bdaddr() { - hcibuf[0] = 0x09; // HCI OCF = 9 - hcibuf[1] = 0x04 << 2; // HCI OGF = 4 - hcibuf[2] = 0x00; - HCI_Command(hcibuf, 3); -} -void RFCOMM::hci_accept_connection() { - hcibuf[0] = 0x09; // HCI OCF = 9 - hcibuf[1] = 0x01 << 2; // HCI OGF = 1 - hcibuf[2] = 0x07; // parameter length 7 - hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr - hcibuf[4] = disc_bdaddr[1]; - hcibuf[5] = disc_bdaddr[2]; - hcibuf[6] = disc_bdaddr[3]; - hcibuf[7] = disc_bdaddr[4]; - hcibuf[8] = disc_bdaddr[5]; - hcibuf[9] = 0x00; //switch role to master - - HCI_Command(hcibuf, 10); -} -void RFCOMM::hci_remote_name() { - hci_event_flag &= ~HCI_FLAG_REMOTE_NAME_COMPLETE; - hcibuf[0] = 0x19; // HCI OCF = 19 - hcibuf[1] = 0x01 << 2; // HCI OGF = 1 - hcibuf[2] = 0x0A; // parameter length = 10 - hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr - hcibuf[4] = disc_bdaddr[1]; - hcibuf[5] = disc_bdaddr[2]; - hcibuf[6] = disc_bdaddr[3]; - hcibuf[7] = disc_bdaddr[4]; - hcibuf[8] = disc_bdaddr[5]; - hcibuf[9] = 0x01; //Page Scan Repetition Mode - hcibuf[10] = 0x00; //Reserved - hcibuf[11] = 0x00; //Clock offset - low byte - hcibuf[12] = 0x00; //Clock offset - high byte - - HCI_Command(hcibuf, 13); -} -void RFCOMM::hci_set_local_name(const char* name) { - hcibuf[0] = 0x13; // HCI OCF = 13 - hcibuf[1] = 0x03 << 2; // HCI OGF = 3 - hcibuf[2] = strlen(name)+1; // parameter length = the length of the string - uint8_t i; - for(i = 0; i < strlen(name); i++) - hcibuf[i+3] = name[i]; - hcibuf[i+3] = 0x00; // End of string - - HCI_Command(hcibuf, 4+strlen(name)); -} -void RFCOMM::hci_pin_code_request_reply(const char* key) { - hcibuf[0] = 0x0D; // HCI OCF = 0D - hcibuf[1] = 0x01 << 2; // HCI OGF = 1 - hcibuf[2] = 0x17; // parameter length 23 - hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr - hcibuf[4] = disc_bdaddr[1]; - hcibuf[5] = disc_bdaddr[2]; - hcibuf[6] = disc_bdaddr[3]; - hcibuf[7] = disc_bdaddr[4]; - hcibuf[8] = disc_bdaddr[5]; - hcibuf[9] = strlen(key); // Length of key - uint8_t i; - for(i = 0; i < strlen(key); i++) // The maximum size of the key is 16 - hcibuf[i+10] = key[i]; - for(;i < 16; i++) - hcibuf[i+10] = 0x00; // The rest should be 0 - - HCI_Command(hcibuf, 26); -} -void RFCOMM::hci_link_key_request_negative_reply() { - hcibuf[0] = 0x0C; // HCI OCF = 0C - hcibuf[1] = 0x01 << 2; // HCI OGF = 1 - hcibuf[2] = 0x06; // parameter length 7 - hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr - hcibuf[4] = disc_bdaddr[1]; - hcibuf[5] = disc_bdaddr[2]; - hcibuf[6] = disc_bdaddr[3]; - hcibuf[7] = disc_bdaddr[4]; - hcibuf[8] = disc_bdaddr[5]; - - HCI_Command(hcibuf, 9); - -} -void RFCOMM::hci_disconnect() { - hci_event_flag &= ~HCI_FLAG_DISCONN_COMPLETE; - hcibuf[0] = 0x06; // HCI OCF = 6 - hcibuf[1] = 0x01 << 2; // HCI OGF = 1 - hcibuf[2] = 0x03; // parameter length = 3 - hcibuf[3] = (uint8_t)(hci_handle & 0xFF);//connection handle - low byte - hcibuf[4] = (uint8_t)((hci_handle >> 8) & 0x0F);//connection handle - high byte - hcibuf[5] = 0x13; // reason - - HCI_Command(hcibuf, 6); -} -/******************************************************************* - * * - * HCI ACL Data Packet * - * * - * buf[0] buf[1] buf[2] buf[3] - * 0 4 8 11 12 16 24 31 MSB - * .-+-+-+-+-+-+-+-|-+-+-+-|-+-|-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. - * | HCI Handle |PB |BC | Data Total Length | HCI ACL Data Packet - * .-+-+-+-+-+-+-+-|-+-+-+-|-+-|-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. - * - * buf[4] buf[5] buf[6] buf[7] - * 0 8 16 31 MSB - * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. - * | Length | Channel ID | Basic L2CAP header - * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. - * - * buf[8] buf[9] buf[10] buf[11] - * 0 8 16 31 MSB - * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. - * | Code | Identifier | Length | Control frame (C-frame) - * .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-. (signaling packet format) - */ -/************************************************************/ -/* L2CAP Commands */ -/************************************************************/ -void RFCOMM::L2CAP_Command(uint8_t* data, uint8_t nbytes, uint8_t channelLow, uint8_t channelHigh) { - uint8_t buf[256]; - buf[0] = (uint8_t)(hci_handle & 0xff); // HCI handle with PB,BC flag - buf[1] = (uint8_t)(((hci_handle >> 8) & 0x0f) | 0x20); - buf[2] = (uint8_t)((4 + nbytes) & 0xff); // HCI ACL total data length - buf[3] = (uint8_t)((4 + nbytes) >> 8); - buf[4] = (uint8_t)(nbytes & 0xff); // L2CAP header: Length - buf[5] = (uint8_t)(nbytes >> 8); - buf[6] = channelLow; - buf[7] = channelHigh; - - for (uint16_t i = 0; i < nbytes; i++)//L2CAP C-frame - buf[8 + i] = data[i]; - - uint8_t rcode = pUsb->outTransfer(bAddress, epInfo[ BTD_DATAOUT_PIPE ].epAddr, (8 + nbytes), buf); - if(rcode) { -#ifdef DEBUG - Notify(PSTR("\r\nError sending L2CAP message: 0x")); - PrintHex(rcode); - Notify(PSTR(" - Channel ID: ")); - Serial.print(channelHigh); - Notify(PSTR(" ")); - Serial.print(channelHigh); -#endif - } -} -void RFCOMM::l2cap_connection_response(uint8_t rxid, uint8_t* dcid, uint8_t* scid, uint8_t result) { - l2capoutbuf[0] = L2CAP_CMD_CONNECTION_RESPONSE;// Code - l2capoutbuf[1] = rxid;// Identifier - l2capoutbuf[2] = 0x08;// Length - l2capoutbuf[3] = 0x00; - l2capoutbuf[4] = dcid[0];// Destination CID - l2capoutbuf[5] = dcid[1]; - l2capoutbuf[6] = scid[0];// Source CID - l2capoutbuf[7] = scid[1]; - l2capoutbuf[8] = result;// Result: Pending or Success - l2capoutbuf[9] = 0x00; - l2capoutbuf[10] = 0x00;// No further information - l2capoutbuf[11] = 0x00; - - L2CAP_Command(l2capoutbuf, 12); -} -void RFCOMM::l2cap_config_request(uint8_t rxid, uint8_t* dcid) { - l2capoutbuf[0] = L2CAP_CMD_CONFIG_REQUEST;// Code - l2capoutbuf[1] = rxid;// Identifier - l2capoutbuf[2] = 0x08;// Length - l2capoutbuf[3] = 0x00; - l2capoutbuf[4] = dcid[0];// Destination CID - l2capoutbuf[5] = dcid[1]; - l2capoutbuf[6] = 0x00;// Flags - l2capoutbuf[7] = 0x00; - l2capoutbuf[8] = 0x01;// Config Opt: type = MTU (Maximum Transmission Unit) - Hint - l2capoutbuf[9] = 0x02;// Config Opt: length - l2capoutbuf[10] = 0xFF;// MTU - l2capoutbuf[11] = 0xFF; - - L2CAP_Command(l2capoutbuf, 12); -} -void RFCOMM::l2cap_config_response(uint8_t rxid, uint8_t* scid) { - l2capoutbuf[0] = L2CAP_CMD_CONFIG_RESPONSE;// Code - l2capoutbuf[1] = rxid;// Identifier - l2capoutbuf[2] = 0x0A;// Length - l2capoutbuf[3] = 0x00; - l2capoutbuf[4] = scid[0];// Source CID - l2capoutbuf[5] = scid[1]; - l2capoutbuf[6] = 0x00;// Flag - l2capoutbuf[7] = 0x00; - l2capoutbuf[8] = 0x00;// Result - l2capoutbuf[9] = 0x00; - l2capoutbuf[10] = 0x01;// Config - l2capoutbuf[11] = 0x02; - l2capoutbuf[12] = 0xA0; - l2capoutbuf[13] = 0x02; - - L2CAP_Command(l2capoutbuf, 14); -} -void RFCOMM::l2cap_disconnection_request(uint8_t rxid, uint8_t* dcid, uint8_t* scid) { - l2cap_event_flag = 0; // Reset flags - l2capoutbuf[0] = L2CAP_CMD_DISCONNECT_REQUEST;// Code - l2capoutbuf[1] = rxid;// Identifier - l2capoutbuf[2] = 0x04;// Length - l2capoutbuf[3] = 0x00; - l2capoutbuf[4] = scid[0];// Really Destination CID - l2capoutbuf[5] = scid[1]; - l2capoutbuf[6] = dcid[0];// Really Source CID - l2capoutbuf[7] = dcid[1]; - L2CAP_Command(l2capoutbuf, 8); -} -void RFCOMM::l2cap_disconnection_response(uint8_t rxid, uint8_t* dcid, uint8_t* scid) { - l2cap_event_flag = 0; // Reset flags - l2capoutbuf[0] = L2CAP_CMD_DISCONNECT_RESPONSE;// Code - l2capoutbuf[1] = rxid;// Identifier - l2capoutbuf[2] = 0x04;// Length - l2capoutbuf[3] = 0x00; - l2capoutbuf[4] = dcid[0]; - l2capoutbuf[5] = dcid[1]; - l2capoutbuf[6] = scid[0]; - l2capoutbuf[7] = scid[1]; - L2CAP_Command(l2capoutbuf, 8); -} -void RFCOMM::l2cap_information_response(uint8_t rxid, uint8_t infoTypeLow, uint8_t infoTypeHigh) { - l2capoutbuf[0] = L2CAP_CMD_INFORMATION_RESPONSE;// Code - l2capoutbuf[1] = rxid;// Identifier - l2capoutbuf[2] = 0x08;// Length - l2capoutbuf[3] = 0x00; - l2capoutbuf[4] = infoTypeLow; - l2capoutbuf[5] = infoTypeHigh; - l2capoutbuf[6] = 0x00; // Result = success - l2capoutbuf[7] = 0x00; // Result = success - l2capoutbuf[8] = 0x00; - l2capoutbuf[9] = 0x00; - l2capoutbuf[10] = 0x00; - l2capoutbuf[11] = 0x00; - L2CAP_Command(l2capoutbuf, 12); -} - -/************************************************************/ -/* SDP Commands */ -/************************************************************/ -void RFCOMM::SDP_Command(uint8_t* data, uint8_t nbytes) { // See page 223 in the Bluetooth specs - L2CAP_Command(data,nbytes,sdp_scid[0],sdp_scid[1]); -} -void RFCOMM::serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow) { // See page 235 in the Bluetooth specs - l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU; - l2capoutbuf[1] = transactionIDHigh; - l2capoutbuf[2] = transactionIDLow; - l2capoutbuf[3] = 0x00; // Parameter Length - l2capoutbuf[4] = 0x05; // Parameter Length - l2capoutbuf[5] = 0x00; // AttributeListsByteCount - l2capoutbuf[6] = 0x02; // AttributeListsByteCount - - /* Attribute ID/Value Sequence: */ - l2capoutbuf[7] = 0x35; - l2capoutbuf[8] = 0x00; - l2capoutbuf[9] = 0x00; - - SDP_Command(l2capoutbuf,10); -} -void RFCOMM::serialPortResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow) { - l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU; - l2capoutbuf[1] = transactionIDHigh; - l2capoutbuf[2] = transactionIDLow; - l2capoutbuf[3] = 0x00; // Parameter Length - l2capoutbuf[4] = 0x2B; // Parameter Length - l2capoutbuf[5] = 0x00; // AttributeListsByteCount - l2capoutbuf[6] = 0x26; // AttributeListsByteCount - - /* Attribute ID/Value Sequence: */ - l2capoutbuf[7] = 0x36; - l2capoutbuf[8] = 0x00; - l2capoutbuf[9] = 0x3C; - l2capoutbuf[10] = 0x36; - l2capoutbuf[11] = 0x00; - - l2capoutbuf[12] = 0x39; - l2capoutbuf[13] = 0x09; - l2capoutbuf[14] = 0x00; - l2capoutbuf[15] = 0x00; - l2capoutbuf[16] = 0x0A; - l2capoutbuf[17] = 0x00; - l2capoutbuf[18] = 0x01; - l2capoutbuf[19] = 0x00; - l2capoutbuf[20] = 0x06; - l2capoutbuf[21] = 0x09; - l2capoutbuf[22] = 0x00; - l2capoutbuf[23] = 0x01; - l2capoutbuf[24] = 0x35; - l2capoutbuf[25] = 0x03; - l2capoutbuf[26] = 0x19; - l2capoutbuf[27] = 0x11; - - l2capoutbuf[28] = 0x01; - l2capoutbuf[29] = 0x09; - l2capoutbuf[30] = 0x00; - l2capoutbuf[31] = 0x04; - l2capoutbuf[32] = 0x35; - l2capoutbuf[33] = 0x0C; - l2capoutbuf[34] = 0x35; - l2capoutbuf[35] = 0x03; - l2capoutbuf[36] = 0x19; - l2capoutbuf[37] = 0x01; - l2capoutbuf[38] = 0x00; - l2capoutbuf[39] = 0x35; - l2capoutbuf[40] = 0x05; - l2capoutbuf[41] = 0x19; - l2capoutbuf[42] = 0x00; - l2capoutbuf[43] = 0x03; - - l2capoutbuf[44] = 0x08; - l2capoutbuf[45] = 0x02; // Two more bytes? - l2capoutbuf[46] = 0x00; // 19 more bytes to come - l2capoutbuf[47] = 0x19; - - SDP_Command(l2capoutbuf,48); -} -void RFCOMM::serialPortResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow) { - l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU; - l2capoutbuf[1] = transactionIDHigh; - l2capoutbuf[2] = transactionIDLow; - l2capoutbuf[3] = 0x00; // Parameter Length - l2capoutbuf[4] = 0x1C; // Parameter Length - l2capoutbuf[5] = 0x00; // AttributeListsByteCount - l2capoutbuf[6] = 0x19; // AttributeListsByteCount - - /* Attribute ID/Value Sequence: */ - l2capoutbuf[7] = 0x01; - l2capoutbuf[8] = 0x09; - l2capoutbuf[9] = 0x00; - l2capoutbuf[10] = 0x06; - l2capoutbuf[11] = 0x35; - - l2capoutbuf[12] = 0x09; - l2capoutbuf[13] = 0x09; - l2capoutbuf[14] = 0x65; - l2capoutbuf[15] = 0x6E; - l2capoutbuf[16] = 0x09; - l2capoutbuf[17] = 0x00; - l2capoutbuf[18] = 0x6A; - l2capoutbuf[19] = 0x09; - l2capoutbuf[20] = 0x01; - l2capoutbuf[21] = 0x00; - l2capoutbuf[22] = 0x09; - l2capoutbuf[23] = 0x01; - l2capoutbuf[24] = 0x00; - l2capoutbuf[25] = 0x25; - - l2capoutbuf[26] = 0x05; // Name length - l2capoutbuf[27] = 'T'; - l2capoutbuf[28] = 'K'; - l2capoutbuf[29] = 'J'; - l2capoutbuf[30] = 'S'; - l2capoutbuf[31] = 'P'; - l2capoutbuf[32] = 0x00; - - SDP_Command(l2capoutbuf,33); -} -void RFCOMM::l2capResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow) { - serialPortResponse1(transactionIDHigh,transactionIDLow); // These has to send all the supported functions, since it only supports virtual serialport it just sends the message again -} -void RFCOMM::l2capResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow) { - serialPortResponse2(transactionIDHigh,transactionIDLow); // Same data as serialPortResponse2 -} -/************************************************************/ -/* RFCOMM Commands */ -/************************************************************/ -void RFCOMM::RFCOMM_Command(uint8_t* data, uint8_t nbytes) { - L2CAP_Command(data,nbytes,rfcomm_scid[0],rfcomm_scid[1]); -} - -void RFCOMM::sendRfcomm(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t* data, uint8_t length) { - l2capoutbuf[0] = channel | direction | CR | extendAddress; // RFCOMM Address - l2capoutbuf[1] = channelType | pfBit; // RFCOMM Control - l2capoutbuf[2] = length << 1 | 0x01; // Length and format (allways 0x01 bytes format) - uint8_t i = 0; - for(; i < length; i++) - l2capoutbuf[i+3] = data[i]; - l2capoutbuf[i+3] = calcFcs(l2capoutbuf); -#ifdef EXTRADEBUG - Notify(PSTR(" - RFCOMM Data: ")); - for(i = 0; i < length+4; i++) { - Serial.print(l2capoutbuf[i],HEX); - Notify(PSTR(" ")); - } -#endif - RFCOMM_Command(l2capoutbuf,length+4); -} - -void RFCOMM::sendRfcommCredit(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t credit) { - l2capoutbuf[0] = channel | direction | CR | extendAddress; // RFCOMM Address - l2capoutbuf[1] = channelType | pfBit; // RFCOMM Control - l2capoutbuf[2] = 0x01; // Length = 0 - l2capoutbuf[3] = credit; // Credit - l2capoutbuf[4] = calcFcs(l2capoutbuf); -#ifdef EXTRADEBUG - Notify(PSTR(" - RFCOMM Credit Data: ")); - for(uint8_t i = 0; i < 5; i++) { - Serial.print(l2capoutbuf[i],HEX); - Notify(PSTR(" ")); - } -#endif - RFCOMM_Command(l2capoutbuf,5); -} - -/* CRC on 2 bytes */ -uint8_t RFCOMM::__crc(uint8_t* data) { - return(pgm_read_byte(&rfcomm_crc_table[pgm_read_byte(&rfcomm_crc_table[0xff ^ data[0]]) ^ data[1]])); -} - -/* Calculate FCS - we never actually check if the host sends correct FCS to the Arduino */ -uint8_t RFCOMM::calcFcs(uint8_t *data) { - if((data[1] & 0xEF) == RFCOMM_UIH) - return (0xff - __crc(data)); // FCS on 2 bytes - else - return (0xff - pgm_read_byte(&rfcomm_crc_table[__crc(data) ^ data[2]])); // FCS on 3 bytes -} - -/* Serial commands */ -void RFCOMM::print(const char* data) { - rfcommbuf[0] = rfcommChannelPermanent | 0 | 0 | extendAddress;; // RFCOMM Address - rfcommbuf[1] = RFCOMM_UIH; // RFCOMM Control - rfcommbuf[2] = strlen(data) << 1 | 1; // Length - uint8_t i = 0; - for(; i < strlen(data); i++) - rfcommbuf[i+3] = data[i]; - rfcommbuf[i+3] = calcFcs(rfcommbuf); - - RFCOMM_Command(rfcommbuf,strlen(data)+4); -} -void RFCOMM::print(uint8_t data) { - print(&data,1); -} -void RFCOMM::print(uint8_t* array, uint8_t length) { - rfcommbuf[0] = rfcommChannelPermanent | 0 | 0 | extendAddress;; // RFCOMM Address - rfcommbuf[1] = RFCOMM_UIH; // RFCOMM Control - rfcommbuf[2] = length << 1 | 1; // Length - uint8_t i = 0; - for(; i < length; i++) - rfcommbuf[i+3] = array[i]; - rfcommbuf[i+3] = calcFcs(rfcommbuf); - - RFCOMM_Command(rfcommbuf,length+4); -} -void RFCOMM::print(const __FlashStringHelper *ifsh) { - const char PROGMEM *p = (const char PROGMEM *)ifsh; - size_t size = 0; - while (1) { // Calculate the size of the string - uint8_t c = pgm_read_byte(p+size); - if (c == 0) - break; - size++; - } - uint8_t buf[size]; - - for(uint8_t i = 0; i < size; i++) - buf[i] = pgm_read_byte(p++); - - print(buf,size); -} - -void RFCOMM::println(const char* data) { - char output[strlen(data)+2]; - strcpy(output,data); - strcat(output,"\r\n"); - print(output); -} -void RFCOMM::println(uint8_t data) { - uint8_t buf[3] = {data, '\r', '\n'}; - print(buf,3); -} -void RFCOMM::println(uint8_t* array, uint8_t length) { - uint8_t buf[length+2]; - memcpy(buf,array,length); - buf[length] = '\r'; - buf[length+1] = '\n'; - print(buf,length+2); -} -void RFCOMM::println(const __FlashStringHelper *ifsh) { - const char PROGMEM *p = (const char PROGMEM *)ifsh; - size_t size = 0; - while (1) { // Calculate the size of the string - uint8_t c = pgm_read_byte(p+size); - if (c == 0) - break; - size++; - } - uint8_t buf[size+2]; - - for(uint8_t i = 0; i < size; i++) - buf[i] = pgm_read_byte(p++); - - buf[size] = '\r'; - buf[size+1] = '\n'; - print(buf,size+2); -} - -uint8_t RFCOMM::read() { - uint8_t output = rfcommDataBuffer[0]; - for(uint8_t i = 1; i < rfcommAvailable; i++) - rfcommDataBuffer[i-1] = rfcommDataBuffer[i]; // Shift the buffer one left - rfcommAvailable--; - return output; -} \ No newline at end of file diff --git a/RFCOMM.h b/RFCOMM.h deleted file mode 100644 index 2500c8e5..00000000 --- a/RFCOMM.h +++ /dev/null @@ -1,339 +0,0 @@ -/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. - - This software may be distributed and modified under the terms of the GNU - General Public License version 2 (GPL2) as published by the Free Software - Foundation and appearing in the file GPL2.TXT included in the packaging of - this file. Please note that GPL2 Section 2[b] requires that all works based - on this software must also be made publicly available under the terms of - the GPL2 ("Copyleft"). - - Contact information - ------------------- - - Kristian Lauszus, TKJ Electronics - Web : http://www.tkjelectronics.com - e-mail : kristianl@tkjelectronics.com - */ - -#ifndef _rfcomm_h_ -#define _rfcomm_h_ - -#if defined(ARDUINO) && ARDUINO >= 100 -#include "Arduino.h" -#else -#include "WProgram.h" -#endif - -#include "Usb.h" -#include "confdescparser.h" - -/* CSR Bluetooth data taken from descriptors */ -#define BULK_MAXPKTSIZE 64 // max size for ACL data - -// used in control endpoint header for HCI Commands -#define bmREQ_HCI_OUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE - -/* Bluetooth HCI states for hci_task() */ -#define HCI_INIT_STATE 0 -#define HCI_RESET_STATE 1 -#define HCI_BDADDR_STATE 2 -#define HCI_SET_NAME_STATE 3 -#define HCI_SCANNING_STATE 4 -#define HCI_CONNECT_IN_STATE 5 -#define HCI_REMOTE_NAME_STATE 6 -#define HCI_CONNECTED_STATE 7 -#define HCI_DISABLE_SCAN 8 -#define HCI_DONE_STATE 9 -#define HCI_DISCONNECT_STATE 10 - -/* HCI event flags*/ -#define HCI_FLAG_CMD_COMPLETE 0x01 -#define HCI_FLAG_CONN_COMPLETE 0x02 -#define HCI_FLAG_DISCONN_COMPLETE 0x04 -#define HCI_FLAG_REMOTE_NAME_COMPLETE 0x08 -#define HCI_FLAG_INCOMING_REQUEST 0x10 -#define HCI_FLAG_READ_BDADDR 0x20 - -/*Macros for HCI event flag tests */ -#define hci_cmd_complete (hci_event_flag & HCI_FLAG_CMD_COMPLETE) -#define hci_connect_complete (hci_event_flag & HCI_FLAG_CONN_COMPLETE) -#define hci_disconnect_complete (hci_event_flag & HCI_FLAG_DISCONN_COMPLETE) -#define hci_remote_name_complete (hci_event_flag & HCI_FLAG_REMOTE_NAME_COMPLETE) -#define hci_incoming_connect_request (hci_event_flag & HCI_FLAG_INCOMING_REQUEST) -#define hci_read_bdaddr_complete (hci_event_flag & HCI_FLAG_READ_BDADDR) - -/* HCI Events managed */ -#define EV_COMMAND_COMPLETE 0x0E -#define EV_COMMAND_STATUS 0x0F -#define EV_CONNECT_COMPLETE 0x03 -#define EV_DISCONNECT_COMPLETE 0x05 -#define EV_NUM_COMPLETE_PKT 0x13 -#define EV_INQUIRY_COMPLETE 0x01 -#define EV_INQUIRY_RESULT 0x02 -#define EV_REMOTE_NAME_COMPLETE 0x07 -#define EV_INCOMING_CONNECT 0x04 -#define EV_ROLE_CHANGED 0x12 -#define EV_PAGE_SCAN_REP_MODE 0x20 -#define EV_DATA_BUFFER_OVERFLOW 0x1A -#define EV_LOOPBACK_COMMAND 0x19 -#define EV_CHANGE_CONNECTION_LINK 0x09 -#define EV_AUTHENTICATION_COMPLETE 0x06 -#define EV_MAX_SLOTS_CHANGE 0x1B -#define EV_PIN_CODE_REQUEST 0x16 -#define EV_LINK_KEY_REQUEST 0x17 -#define EV_QOS_SETUP_COMPLETE 0x0D -#define EV_LINK_KEY_NOTIFICATION 0x18 -#define EV_ENCRYPTION_CHANGE 0x08 -#define EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE 0x0C - -/* Bluetooth L2CAP states for SDP_task() and RFCOMM_task() */ -#define L2CAP_SDP_WAIT 0 -#define L2CAP_SDP_SETUP 1 -#define L2CAP_SDP_REQUEST 2 -#define L2CAP_SDP_SUCCESS 3 -#define L2CAP_SDP_DONE 4 -#define L2CAP_RFCOMM_WAIT 5 -#define L2CAP_RFCOMM_SETUP 6 -#define L2CAP_RFCOMM_REQUEST 7 -#define L2CAP_RFCOMM_SUCCESS 8 -#define L2CAP_RFCOMM_DONE 9 -#define L2CAP_DISCONNECT_RESPONSE 10 - -/* L2CAP event flags */ -#define L2CAP_FLAG_CONNECTION_SDP_REQUEST 0x001 -#define L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST 0x002 -#define L2CAP_FLAG_CONFIG_SDP_REQUEST 0x004 -#define L2CAP_FLAG_CONFIG_RFCOMM_REQUEST 0x008 -#define L2CAP_FLAG_CONFIG_SDP_SUCCESS 0x010 -#define L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS 0x020 -#define L2CAP_FLAG_DISCONNECT_SDP_REQUEST 0x040 -#define L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST 0x080 -#define L2CAP_FLAG_DISCONNECT_RESPONSE 0x100 - -/* Macros for L2CAP event flag tests */ -#define l2cap_connection_request_sdp_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_SDP_REQUEST) -#define l2cap_connection_request_rfcomm_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST) -#define l2cap_config_request_sdp_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_SDP_REQUEST) -#define l2cap_config_request_rfcomm_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_RFCOMM_REQUEST) -#define l2cap_config_success_sdp_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_SDP_SUCCESS) -#define l2cap_config_success_rfcomm_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS) -#define l2cap_disconnect_request_sdp_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_SDP_REQUEST) -#define l2cap_disconnect_request_rfcomm_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST) -#define l2cap_disconnect_response_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_RESPONSE) - -/* L2CAP signaling commands */ -#define L2CAP_CMD_COMMAND_REJECT 0x01 -#define L2CAP_CMD_CONNECTION_REQUEST 0x02 -#define L2CAP_CMD_CONNECTION_RESPONSE 0x03 -#define L2CAP_CMD_CONFIG_REQUEST 0x04 -#define L2CAP_CMD_CONFIG_RESPONSE 0x05 -#define L2CAP_CMD_DISCONNECT_REQUEST 0x06 -#define L2CAP_CMD_DISCONNECT_RESPONSE 0x07 -#define L2CAP_CMD_INFORMATION_REQUEST 0x0A -#define L2CAP_CMD_INFORMATION_RESPONSE 0x0B - -/* Bluetooth L2CAP PSM */ -#define SDP_PSM 0x01 // Service Discovery Protocol PSM Value -#define RFCOMM_PSM 0x03 // RFCOMM PSM Value - -// Used For Connection Response - Remember to Include High Byte -#define PENDING 0x01 -#define SUCCESSFUL 0x00 - -// Used to determine if it is a Bluetooth dongle -#define WI_SUBCLASS_RF 0x01 // RF Controller -#define WI_PROTOCOL_BT 0x01 // Bluetooth Programming Interface - -#define BTD_MAX_ENDPOINTS 4 - -/* Used for SDP */ -#define SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU 0x06 // See the RFCOMM specs -#define SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU 0x07 // See the RFCOMM specs -#define SERIALPORT_UUID 0x1101 // See http://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm -#define L2CAP_UUID 0x0100 - -/* Used for RFCOMM */ -#define RFCOMM_SABM 0x2F -#define RFCOMM_UA 0x63 -#define RFCOMM_UIH 0xEF -//#define RFCOMM_DM 0x0F -#define RFCOMM_DISC 0x43 - -#define extendAddress 0x01 // Allways 1 - -// Multiplexer message types -#define BT_RFCOMM_PN_CMD 0x83 -#define BT_RFCOMM_PN_RSP 0x81 -#define BT_RFCOMM_MSC_CMD 0xE3 -#define BT_RFCOMM_MSC_RSP 0xE1 -#define BT_RFCOMM_RPN_CMD 0x93 -#define BT_RFCOMM_RPN_RSP 0x91 -/* -#define BT_RFCOMM_TEST_CMD 0x23 -#define BT_RFCOMM_TEST_RSP 0x21 -#define BT_RFCOMM_FCON_CMD 0xA3 -#define BT_RFCOMM_FCON_RSP 0xA1 -#define BT_RFCOMM_FCOFF_CMD 0x63 -#define BT_RFCOMM_FCOFF_RSP 0x61 -#define BT_RFCOMM_RLS_CMD 0x53 -#define BT_RFCOMM_RLS_RSP 0x51 -#define BT_RFCOMM_NSC_RSP 0x11 -*/ - -class RFCOMM : public USBDeviceConfig, public UsbConfigXtracter { -public: - RFCOMM(USB *p, const char* name = "Arduino", const char* pin = "1234"); - - // USBDeviceConfig implementation - virtual uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed); - virtual uint8_t Release(); - virtual uint8_t Poll(); - virtual uint8_t GetAddress() { return bAddress; }; - virtual bool isReady() { return bPollEnable; }; - - // UsbConfigXtracter implementation - virtual void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep); - - bool isWatingForConnection() { return watingForConnection; }; // Use this to indicate when it is ready for a incoming connection - - void disconnect(); // Used this void to disconnect the virtual serial port - bool connected;// Variable used to indicate if the connection is established - - /* Serial commands currently supported */ - void print(const char* data); // Used to send strings - void print(uint8_t data); // Used to send single bytes - void print(uint8_t* array, uint8_t length); // Used to send arrays - void print(const __FlashStringHelper *); // Used to print strings stored in flash - - void println(const char* data); // Include newline and carriage return - void println(uint8_t data); // Include newline and carriage return - void println(uint8_t* array, uint8_t length); // Include newline and carriage return - void println(const __FlashStringHelper *); // Include newline and carriage return - - uint8_t available() { return rfcommAvailable; }; // Get the bytes waiting to be read - uint8_t read(); // Used to read the buffer - -protected: - /* mandatory members */ - USB *pUsb; - uint8_t bAddress; // device address - EpInfo epInfo[BTD_MAX_ENDPOINTS]; //endpoint info structure - - uint8_t bConfNum; // configuration number - uint8_t bNumEP; // total number of endpoints in the configuration - uint32_t qNextPollTime; // next poll time - - #define BTD_CONTROL_PIPE 0 // Bluetooth dongles control endpoint - static const uint8_t BTD_EVENT_PIPE; // HCI event endpoint index - static const uint8_t BTD_DATAIN_PIPE; // ACL In endpoint index - static const uint8_t BTD_DATAOUT_PIPE; // ACL Out endpoint index - - void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr); - -private: - const char* btdName; - const char* btdPin; - - bool bPollEnable; - uint8_t pollInterval; - bool watingForConnection; - - /* Set true when a channel is created */ - bool SDPConnected; - bool RFCOMMConnected; - - /*variables filled from HCI event management */ - uint16_t hci_handle; - uint8_t my_bdaddr[6]; // The bluetooth dongles Bluetooth address - uint8_t disc_bdaddr[6]; // the bluetooth address is always 6 bytes - uint8_t remote_name[30]; // first 30 chars of remote name - - /* variables used by high level HCI task */ - uint8_t hci_state; //current state of bluetooth hci connection - uint16_t hci_counter; // counter used for bluetooth hci reset loops - uint8_t hci_num_reset_loops; // this value indicate how many times it should read before trying to reset - uint16_t hci_event_flag;// hci flags of received bluetooth events - - /* variables used by high level L2CAP task */ - uint8_t l2cap_sdp_state; - uint8_t l2cap_rfcomm_state; - uint16_t l2cap_event_flag;// l2cap flags of received bluetooth events - - uint8_t hcibuf[BULK_MAXPKTSIZE];//General purpose buffer for hci data - uint8_t l2capinbuf[BULK_MAXPKTSIZE];//General purpose buffer for l2cap in data - uint8_t l2capoutbuf[BULK_MAXPKTSIZE];//General purpose buffer for l2cap out data - uint8_t rfcommbuf[BULK_MAXPKTSIZE]; // Buffer for RFCOMM Data - - /* L2CAP Channels */ - uint8_t sdp_scid[2]; // L2CAP source CID for SDP - uint8_t sdp_dcid[2]; // 0x0050 - uint8_t rfcomm_scid[2]; // L2CAP source CID for RFCOMM - uint8_t rfcomm_dcid[2]; // 0x0051 - uint8_t identifier; // Identifier for command - - /* RFCOMM Variables */ - uint8_t rfcommChannel; - uint8_t rfcommChannelPermanent; - uint8_t rfcommDirection; - uint8_t rfcommCommandResponse; - uint8_t rfcommChannelType; - uint8_t rfcommPfBit; - - unsigned long timer; - bool waitForLastCommand; - bool creditSent; - - uint8_t rfcommDataBuffer[256]; // Create a 256 sized buffer for incoming data - uint8_t rfcommAvailable; - - bool firstMessage; // Used to see if it's the first SDP request received - uint8_t bytesReceived; // Counter to see when it's time to send more credit - - /* State machines */ - void HCI_event_task(); //poll the HCI event pipe - void HCI_task(); // HCI state machine - void ACL_event_task(); // start polling the ACL input pipe too, though discard data until connected - void SDP_task(); // SDP state machine - void RFCOMM_task(); // RFCOMM state machine - - void readReport(); // read incoming data - void printReport(); // print incoming date - Uncomment "#define PRINTREPORT" to print incoming data debugging - - /* HCI Commands */ - void HCI_Command(uint8_t* data, uint16_t nbytes); - void hci_reset(); - void hci_write_scan_enable(); - void hci_write_scan_disable(); - void hci_read_bdaddr(); - void hci_accept_connection(); - void hci_remote_name(); - void hci_set_local_name(const char* name); - void hci_pin_code_request_reply(const char* key); - void hci_link_key_request_negative_reply(); - void hci_disconnect(); - - /* L2CAP Commands */ - void L2CAP_Command(uint8_t* data, uint8_t nbytes, uint8_t channelLow = 0x01, uint8_t channelHigh = 0x00); // Standard L2CAP header: Channel ID (0x01) for ACL-U - void l2cap_connection_response(uint8_t rxid, uint8_t* dcid, uint8_t* scid, uint8_t result); - void l2cap_config_request(uint8_t rxid, uint8_t* dcid); - void l2cap_config_response(uint8_t rxid, uint8_t* scid); - void l2cap_disconnection_request(uint8_t rxid, uint8_t* dcid, uint8_t* scid); - void l2cap_disconnection_response(uint8_t rxid, uint8_t* dcid, uint8_t* scid); - void l2cap_information_response(uint8_t rxid, uint8_t infoTypeLow, uint8_t infoTypeHigh); - - /* SDP Commands */ - void SDP_Command(uint8_t* data, uint8_t nbytes); - void serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow); - void serialPortResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow); - void serialPortResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow); - void l2capResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow); - void l2capResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow); - - /* RFCOMM Commands */ - void RFCOMM_Command(uint8_t* data, uint8_t nbytes); - void sendRfcomm(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t* data, uint8_t length); - void sendRfcommCredit(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t credit); - uint8_t calcFcs(uint8_t *data); - uint8_t __crc(uint8_t* data); -}; -#endif \ No newline at end of file diff --git a/SPP.cpp b/SPP.cpp new file mode 100644 index 00000000..f74f597c --- /dev/null +++ b/SPP.cpp @@ -0,0 +1,766 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#include "SPP.h" +#define DEBUG // Uncomment to print data for debugging +//#define EXTRADEBUG // Uncomment to get even more debugging data +//#define PRINTREPORT // Uncomment to print the report sent to the Arduino + +/* + * CRC (reversed crc) lookup table as calculated by the table generator in ETSI TS 101 369 V6.3.0. + */ +const uint8_t rfcomm_crc_table[256] PROGMEM = { /* reversed, 8-bit, poly=0x07 */ + 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B, + 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67, + 0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43, + 0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F, + 0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B, + 0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17, + 0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33, + 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F, + 0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B, + 0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87, + 0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3, + 0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF, + 0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB, + 0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7, + 0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3, + 0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF +}; + +SPP::SPP(BTD *p, const char* name, const char* pin): +pBtd(p) // Pointer to BTD class instance - mandatory +{ + if (pBtd) + pBtd->registerServiceClass(this); // Register it as a Bluetooth service + + pBtd->btdName = name; + pBtd->btdPin = pin; + l2cap_sdp_state = L2CAP_SDP_WAIT; + l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT; + l2cap_event_flag = 0; + + /* Set device cid for the SDP and RFCOMM channelse */ + sdp_dcid[0] = 0x50; // 0x0050 + sdp_dcid[1] = 0x00; + rfcomm_dcid[0] = 0x51; // 0x0051 + rfcomm_dcid[1] = 0x00; +} +void SPP::Release() { + connected = false; + RFCOMMConnected = false; + SDPConnected = false; + l2cap_sdp_state = L2CAP_SDP_WAIT; + l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT; + l2cap_event_flag = 0; + +} +void SPP::disconnect(){ + connected = false; + // First the two L2CAP channels has to be disconencted and then the HCI connection + if(RFCOMMConnected) + pBtd->l2cap_disconnection_request(hci_handle,0x0A, rfcomm_scid, rfcomm_dcid); + if(SDPConnected) + pBtd->l2cap_disconnection_request(hci_handle,0x0B, sdp_scid, sdp_dcid); + l2cap_event_flag = 0; // Reset flags + l2cap_sdp_state = L2CAP_DISCONNECT_RESPONSE; +} +void SPP::ACLData(uint8_t* l2capinbuf) { + if (l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) { + if(((l2capinbuf[12] | (l2capinbuf[13] << 8)) == SDP_PSM) || ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == RFCOMM_PSM)) { + if(!pBtd->connectionClaimed && !connected && !RFCOMMConnected && !SDPConnected) { + pBtd->claimConnection(); // Claim that the incoming connection belongs to this service + hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection + } + } + } + if (((l2capinbuf[0] | (l2capinbuf[1] << 8)) == (hci_handle | 0x2000))) { // acl_handle_ok + if ((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001) { //l2cap_control - Channel ID for ACL-U + if (l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) { +#ifdef DEBUG + Notify(PSTR("\r\nL2CAP Command Rejected - Reason: ")); + PrintHex(l2capinbuf[13]); + Notify(PSTR(" ")); + PrintHex(l2capinbuf[12]); + Notify(PSTR(" Data: ")); + PrintHex(l2capinbuf[17]); + Notify(PSTR(" ")); + PrintHex(l2capinbuf[16]); + Notify(PSTR(" ")); + PrintHex(l2capinbuf[15]); + Notify(PSTR(" ")); + PrintHex(l2capinbuf[14]); +#endif + } else if (l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nL2CAP Connection Request - PSM: ")); + PrintHex(l2capinbuf[13]); + Notify(PSTR(" ")); + PrintHex(l2capinbuf[12]); + Notify(PSTR(" SCID: ")); + PrintHex(l2capinbuf[15]); + Notify(PSTR(" ")); + PrintHex(l2capinbuf[14]); + Notify(PSTR(" Identifier: ")); + PrintHex(l2capinbuf[9]); +#endif + if ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == SDP_PSM) { // It doesn't matter if it receives another reqeust, since it waits for the channel to disconnect in the L2CAP_SDP_DONE state, and the l2cap_event_flag will be cleared if so + identifier = l2capinbuf[9]; + sdp_scid[0] = l2capinbuf[14]; + sdp_scid[1] = l2capinbuf[15]; + l2cap_event_flag |= L2CAP_FLAG_CONNECTION_SDP_REQUEST; + } else if ((l2capinbuf[12] | (l2capinbuf[13] << 8)) == RFCOMM_PSM) { // ----- || ----- + identifier = l2capinbuf[9]; + rfcomm_scid[0] = l2capinbuf[14]; + rfcomm_scid[1] = l2capinbuf[15]; + l2cap_event_flag |= L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST; + } + } else if (l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) { + if (l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) { + if ((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success + //Serial.print("\r\nSDP Configuration Complete"); + l2cap_event_flag |= L2CAP_FLAG_CONFIG_SDP_SUCCESS; + } + } + else if (l2capinbuf[12] == rfcomm_dcid[0] && l2capinbuf[13] == rfcomm_dcid[1]) { + if ((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success + //Serial.print("\r\nRFCOMM Configuration Complete"); + l2cap_event_flag |= L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS; + } + } + } else if (l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) { + if (l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) { + //Serial.print("\r\nSDP Configuration Request"); + identifier = l2capinbuf[9]; + l2cap_event_flag |= L2CAP_FLAG_CONFIG_SDP_REQUEST; + } + else if (l2capinbuf[12] == rfcomm_dcid[0] && l2capinbuf[13] == rfcomm_dcid[1]) { + //Serial.print("\r\nRFCOMM Configuration Request"); + identifier = l2capinbuf[9]; + l2cap_event_flag |= L2CAP_FLAG_CONFIG_RFCOMM_REQUEST; + } + } else if (l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) { + if (l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) { + //Notify(PSTR("\r\nDisconnect Request: SDP Channel")); + identifier = l2capinbuf[9]; + l2cap_event_flag |= L2CAP_FLAG_DISCONNECT_SDP_REQUEST; + } else if (l2capinbuf[12] == rfcomm_dcid[0] && l2capinbuf[13] == rfcomm_dcid[1]) { + //Notify(PSTR("\r\nDisconnect Request: RFCOMM Channel")); + identifier = l2capinbuf[9]; + l2cap_event_flag |= L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST; + } + } else if (l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) { + if (l2capinbuf[12] == sdp_scid[0] && l2capinbuf[13] == sdp_scid[1]) { + //Serial.print("\r\nDisconnect Response: SDP Channel"); + identifier = l2capinbuf[9]; + l2cap_event_flag |= L2CAP_FLAG_DISCONNECT_RESPONSE; + } else if (l2capinbuf[12] == rfcomm_scid[0] && l2capinbuf[13] == rfcomm_scid[1]) { + //Serial.print("\r\nDisconnect Response: RFCOMM Channel"); + identifier = l2capinbuf[9]; + l2cap_event_flag |= L2CAP_FLAG_DISCONNECT_RESPONSE; + } + } else if (l2capinbuf[8] == L2CAP_CMD_INFORMATION_REQUEST) { +#ifdef DEBUG + Notify(PSTR("\r\nInformation request")); +#endif + identifier = l2capinbuf[9]; + pBtd->l2cap_information_response(hci_handle,identifier,l2capinbuf[12],l2capinbuf[13]); + } +#ifdef EXTRADEBUG + else { + Notify(PSTR("\r\nL2CAP Unknown Signaling Command: ")); + PrintHex(l2capinbuf[8]); + } +#endif + } else if (l2capinbuf[6] == sdp_dcid[0] && l2capinbuf[7] == sdp_dcid[1]) { // SDP + if(l2capinbuf[8] == SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU) { + /* + Serial.print("\r\nUUID: 0x"); + Serial.print(l2capinbuf[16],HEX); + Serial.print(" "); + Serial.print(l2capinbuf[17],HEX); + */ + if ((l2capinbuf[16] << 8 | l2capinbuf[17]) == SERIALPORT_UUID) { + if(firstMessage) { + serialPortResponse1(l2capinbuf[9],l2capinbuf[10]); + firstMessage = false; + } else { + serialPortResponse2(l2capinbuf[9],l2capinbuf[10]); // Serialport continuation state + firstMessage = true; + } + } else if ((l2capinbuf[16] << 8 | l2capinbuf[17]) == L2CAP_UUID) { + if(firstMessage) { + l2capResponse1(l2capinbuf[9],l2capinbuf[10]); + firstMessage = false; + } else { + l2capResponse2(l2capinbuf[9],l2capinbuf[10]); // L2CAP continuation state + firstMessage = true; + } + } else + serviceNotSupported(l2capinbuf[9],l2capinbuf[10]); // The service is not supported + } + } else if (l2capinbuf[6] == rfcomm_dcid[0] && l2capinbuf[7] == rfcomm_dcid[1]) { // RFCOMM + rfcommChannel = l2capinbuf[8] & 0xF8; + rfcommDirection = l2capinbuf[8] & 0x04; + rfcommCommandResponse = l2capinbuf[8] & 0x02; + rfcommChannelType = l2capinbuf[9] & 0xEF; + rfcommPfBit = l2capinbuf[9] & 0x10; + + if(rfcommChannel>>3 != 0x00) + rfcommChannelConnection = rfcommChannel; + +#ifdef EXTRADEBUG + Notify(PSTR("\r\nRFCOMM Channel: ")); + Serial.print(rfcommChannel>>3,HEX); + Notify(PSTR(" Direction: ")); + Serial.print(rfcommDirection>>2,HEX); + Notify(PSTR(" CommandResponse: ")); + Serial.print(rfcommCommandResponse>>1,HEX); + Notify(PSTR(" ChannelType: ")); + Serial.print(rfcommChannelType,HEX); + Notify(PSTR(" PF_BIT: ")); + Serial.print(rfcommPfBit,HEX); +#endif + if (rfcommChannelType == RFCOMM_DISC) { +#ifdef DEBUG + Notify(PSTR("\r\nReceived Disconnect RFCOMM Command on channel: ")); + Serial.print(rfcommChannel>>3,HEX); +#endif + connected = false; + sendRfcomm(rfcommChannel,rfcommDirection,rfcommCommandResponse,RFCOMM_UA,rfcommPfBit,rfcommbuf,0x00); // UA Command + } + if(connected) { + /* Read the incoming message */ + if(rfcommChannelType != RFCOMM_UIH || rfcommChannel != rfcommChannelConnection) + return; + uint8_t length = l2capinbuf[10] >> 1; // Get length + if(rfcommAvailable + length > 256) + return; // Return if the buffer would be full + uint8_t offset = l2capinbuf[4]-length-4; // See if there is credit + for(uint8_t i = 0; i < length; i++) + rfcommDataBuffer[rfcommAvailable+i] = l2capinbuf[11+i+offset]; + rfcommAvailable += length; +#ifdef EXTRADEBUG + Notify(PSTR("\r\nRFCOMM Data Available: ")); + Serial.print(rfcommAvailable); + if (offset) { + Notify(PSTR(" - Credit: 0x")); + Serial.print(l2capinbuf[11],HEX); + } +#endif +#ifdef PRINTREPORT // Uncomment "#define PRINTREPORT" to print the report send to the Arduino via Bluetooth + if(rfcommChannelType != RFCOMM_UIH || rfcommChannel != rfcommChannelConnection) + return; + uint8_t length = l2capinbuf[10] >> 1; // Get length + uint8_t offset = l2capinbuf[4]-length-4; // See if there is credit + for(uint8_t i = 0; i < length; i++) + Serial.write(l2capinbuf[i+11+offset]); +#endif + } else { + if(rfcommChannelType == RFCOMM_SABM) { // SABM Command - this is sent twice: once for channel 0 and then for the channel to establish +#ifdef DEBUG + Notify(PSTR("\r\nReceived SABM Command")); +#endif + sendRfcomm(rfcommChannel,rfcommDirection,rfcommCommandResponse,RFCOMM_UA,rfcommPfBit,rfcommbuf,0x00); // UA Command + } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_PN_CMD) { // UIH Parameter Negotiation Command +#ifdef DEBUG + Notify(PSTR("\r\nReceived UIH Parameter Negotiation Command")); +#endif + rfcommbuf[0] = BT_RFCOMM_PN_RSP; // UIH Parameter Negotiation Response + rfcommbuf[1] = l2capinbuf[12]; // Length and shiftet like so: length << 1 | 1 + rfcommbuf[2] = l2capinbuf[13]; // Channel: channel << 1 | 1 + rfcommbuf[3] = 0xE0; // Pre difined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM + rfcommbuf[4] = 0x00; // Priority + rfcommbuf[5] = 0x00; // Timer + rfcommbuf[6] = 0x40; // Max Fram Size LSB - we will just set this to 64 + rfcommbuf[7] = 0x00; // Max Fram Size MSB + rfcommbuf[8] = 0x00; // MaxRatransm. + rfcommbuf[9] = 0x00; // Number of Frames + sendRfcomm(rfcommChannel,rfcommDirection,0,RFCOMM_UIH,rfcommPfBit,rfcommbuf,0x0A); + } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_MSC_CMD) { // UIH Modem Status Command +#ifdef DEBUG + Notify(PSTR("\r\nSend UIH Modem Status Response")); +#endif + rfcommbuf[0] = BT_RFCOMM_MSC_RSP; // UIH Modem Status Response + rfcommbuf[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1 + rfcommbuf[2] = l2capinbuf[13]; // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3) + rfcommbuf[3] = l2capinbuf[14]; + sendRfcomm(rfcommChannel,rfcommDirection,0,RFCOMM_UIH,rfcommPfBit,rfcommbuf,0x04); + + delay(1); +#ifdef DEBUG + Notify(PSTR("\r\nSend UIH Modem Status Command")); +#endif + rfcommbuf[0] = BT_RFCOMM_MSC_CMD; // UIH Modem Status Command + rfcommbuf[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1 + rfcommbuf[2] = l2capinbuf[13]; // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3) + rfcommbuf[3] = 0x8D; // Can receive frames (YES), Ready to Communicate (YES), Ready to Receive (YES), Incomig Call (NO), Data is Value (YES) + + sendRfcomm(rfcommChannel,rfcommDirection,0,RFCOMM_UIH,rfcommPfBit,rfcommbuf,0x04); + } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_MSC_RSP) { // UIH Modem Status Response + if(!creditSent) { +#ifdef DEBUG + Notify(PSTR("\r\nSend UIH Command with credit")); +#endif + sendRfcommCredit(rfcommChannelConnection,rfcommDirection,0,RFCOMM_UIH,0x10,0xFF); // 255 credit + creditSent = true; + timer = millis(); + waitForLastCommand = true; + } + } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[10] == 0x01) { // UIH Command with credit +#ifdef DEBUG + Notify(PSTR("\r\nReceived UIH Command with credit")); +#endif + } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_RPN_CMD) { // UIH Remote Port Negotiation Command +#ifdef DEBUG + Notify(PSTR("\r\nReceived UIH Remote Port Negotiation Command")); +#endif + rfcommbuf[0] = BT_RFCOMM_RPN_RSP; // Command + rfcommbuf[1] = l2capinbuf[12]; // Length and shiftet like so: length << 1 | 1 + rfcommbuf[2] = l2capinbuf[13]; // Channel: channel << 1 | 1 + rfcommbuf[3] = l2capinbuf[14]; // Pre difined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM + rfcommbuf[4] = l2capinbuf[15]; // Priority + rfcommbuf[5] = l2capinbuf[16]; // Timer + rfcommbuf[6] = l2capinbuf[17]; // Max Fram Size LSB + rfcommbuf[7] = l2capinbuf[18]; // Max Fram Size MSB + rfcommbuf[8] = l2capinbuf[19]; // MaxRatransm. + rfcommbuf[9] = l2capinbuf[20]; // Number of Frames + sendRfcomm(rfcommChannel,rfcommDirection,0,RFCOMM_UIH,rfcommPfBit,rfcommbuf,0x0A); // UIH Remote Port Negotiation Response +#ifdef DEBUG + Notify(PSTR("\r\nRFCOMM Connection is now established\r\n")); +#endif + waitForLastCommand = false; + creditSent = false; + connected = true; // The RFCOMM channel is now established + } else if(rfcommChannelType != RFCOMM_DISC) { +#ifdef DEBUG + Notify(PSTR("\r\nUnsupported RFCOMM Data - ChannelType: ")); + PrintHex(rfcommChannelType); + Notify(PSTR(" Command: ")); + PrintHex(l2capinbuf[11]); +#endif + } + } + } else { +#ifdef EXTRADEBUG + Notify(PSTR("\r\nUnsupported L2CAP Data - Channel ID: ")); + PrintHex(l2capinbuf[7]); + Notify(PSTR(" ")); + PrintHex(l2capinbuf[6]); +#endif + } + if(waitForLastCommand && (millis() - timer) > 100) { // We will only wait 100ms and see if the UIH Remote Port Negotiation Command is send, as some deviced don't send it +#ifdef DEBUG + Notify(PSTR("\r\nRFCOMM Connection is now established - Automatic\r\n")); +#endif + creditSent = false; + waitForLastCommand = false; + connected = true; // The RFCOMM channel is now established + } + } +} +void SPP::Poll() { + SDP_task(); + RFCOMM_task(); +} +void SPP::SDP_task() { + switch (l2cap_sdp_state) + { + case L2CAP_SDP_WAIT: + if (l2cap_connection_request_sdp_flag) { +#ifdef DEBUG + Notify(PSTR("\r\nSDP Incoming Connection Request")); +#endif + pBtd->l2cap_connection_response(hci_handle,identifier, sdp_dcid, sdp_scid, PENDING); + delay(1); + pBtd->l2cap_connection_response(hci_handle,identifier, sdp_dcid, sdp_scid, SUCCESSFUL); + identifier++; + delay(1); + pBtd->l2cap_config_request(hci_handle,identifier, sdp_scid); + l2cap_sdp_state = L2CAP_SDP_REQUEST; + } + break; + case L2CAP_SDP_REQUEST: + if (l2cap_config_request_sdp_flag) { +#ifdef DEBUG + Notify(PSTR("\r\nSDP Configuration Request")); +#endif + pBtd->l2cap_config_response(hci_handle,identifier, sdp_scid); + l2cap_sdp_state = L2CAP_SDP_SUCCESS; + } + break; + case L2CAP_SDP_SUCCESS: + if (l2cap_config_success_sdp_flag) { +#ifdef DEBUG + Notify(PSTR("\r\nSDP Successfully Configured")); +#endif + firstMessage = true; // Reset bool + SDPConnected = true; + l2cap_sdp_state = L2CAP_SDP_DONE; + } + break; + case L2CAP_SDP_DONE: + if(l2cap_disconnect_request_sdp_flag) { + SDPConnected = false; +#ifdef DEBUG + Notify(PSTR("\r\nDisconnected SDP Channel")); +#endif + pBtd->l2cap_disconnection_response(hci_handle,identifier,sdp_dcid,sdp_scid); + l2cap_event_flag = 0; // Reset flags + l2cap_sdp_state = L2CAP_SDP_WAIT; + } + break; + case L2CAP_DISCONNECT_RESPONSE: // This is for both disconnection response from the RFCOMM and SDP channel if they were connected + if (l2cap_disconnect_response_flag) { +#ifdef DEBUG + Notify(PSTR("\r\nDisconnected L2CAP Connection")); +#endif + RFCOMMConnected = false; + SDPConnected = false; + pBtd->hci_disconnect(hci_handle); + l2cap_sdp_state = L2CAP_SDP_WAIT; + l2cap_sdp_state = L2CAP_RFCOMM_WAIT; + } + break; + } +} +void SPP::RFCOMM_task() +{ + switch (l2cap_rfcomm_state) + { + case L2CAP_RFCOMM_WAIT: + if(l2cap_connection_request_rfcomm_flag) { +#ifdef DEBUG + Notify(PSTR("\r\nRFCOMM Incoming Connection Request")); +#endif + pBtd->l2cap_connection_response(hci_handle,identifier, rfcomm_dcid, rfcomm_scid, PENDING); + delay(1); + pBtd->l2cap_connection_response(hci_handle,identifier, rfcomm_dcid, rfcomm_scid, SUCCESSFUL); + identifier++; + delay(1); + pBtd->l2cap_config_request(hci_handle,identifier, rfcomm_scid); + l2cap_rfcomm_state = L2CAP_RFCOMM_REQUEST; + } + break; + case L2CAP_RFCOMM_REQUEST: + if (l2cap_config_request_rfcomm_flag) { +#ifdef DEBUG + Notify(PSTR("\r\nRFCOMM Configuration Request")); +#endif + pBtd->l2cap_config_response(hci_handle,identifier, rfcomm_scid); + l2cap_rfcomm_state = L2CAP_RFCOMM_SUCCESS; + } + break; + case L2CAP_RFCOMM_SUCCESS: + if (l2cap_config_success_rfcomm_flag) { +#ifdef DEBUG + Notify(PSTR("\r\nRFCOMM Successfully Configured")); +#endif + rfcommAvailable = 0; // Reset number of bytes available + bytesRead = 0; // Reset number of bytes received + RFCOMMConnected = true; + l2cap_rfcomm_state = L2CAP_RFCOMM_DONE; + } + break; + case L2CAP_RFCOMM_DONE: + if(l2cap_disconnect_request_rfcomm_flag) { + RFCOMMConnected = false; + connected = false; +#ifdef DEBUG + Notify(PSTR("\r\nDisconnected RFCOMM Channel")); +#endif + pBtd->l2cap_disconnection_response(hci_handle,identifier,rfcomm_dcid,rfcomm_scid); + l2cap_event_flag = 0; // Reset flags + l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT; + } + break; + } +} +/************************************************************/ +/* SDP Commands */ +/************************************************************/ +void SPP::SDP_Command(uint8_t* data, uint8_t nbytes) { // See page 223 in the Bluetooth specs + pBtd->L2CAP_Command(hci_handle,data,nbytes,sdp_scid[0],sdp_scid[1]); +} +void SPP::serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow) { // See page 235 in the Bluetooth specs + l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU; + l2capoutbuf[1] = transactionIDHigh; + l2capoutbuf[2] = transactionIDLow; + l2capoutbuf[3] = 0x00; // Parameter Length + l2capoutbuf[4] = 0x05; // Parameter Length + l2capoutbuf[5] = 0x00; // AttributeListsByteCount + l2capoutbuf[6] = 0x02; // AttributeListsByteCount + + /* Attribute ID/Value Sequence: */ + l2capoutbuf[7] = 0x35; + l2capoutbuf[8] = 0x00; + l2capoutbuf[9] = 0x00; + + SDP_Command(l2capoutbuf,10); +} +void SPP::serialPortResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow) { + l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU; + l2capoutbuf[1] = transactionIDHigh; + l2capoutbuf[2] = transactionIDLow; + l2capoutbuf[3] = 0x00; // Parameter Length + l2capoutbuf[4] = 0x2B; // Parameter Length + l2capoutbuf[5] = 0x00; // AttributeListsByteCount + l2capoutbuf[6] = 0x26; // AttributeListsByteCount + + /* Attribute ID/Value Sequence: */ + l2capoutbuf[7] = 0x36; + l2capoutbuf[8] = 0x00; + l2capoutbuf[9] = 0x3C; + l2capoutbuf[10] = 0x36; + l2capoutbuf[11] = 0x00; + + l2capoutbuf[12] = 0x39; + l2capoutbuf[13] = 0x09; + l2capoutbuf[14] = 0x00; + l2capoutbuf[15] = 0x00; + l2capoutbuf[16] = 0x0A; + l2capoutbuf[17] = 0x00; + l2capoutbuf[18] = 0x01; + l2capoutbuf[19] = 0x00; + l2capoutbuf[20] = 0x06; + l2capoutbuf[21] = 0x09; + l2capoutbuf[22] = 0x00; + l2capoutbuf[23] = 0x01; + l2capoutbuf[24] = 0x35; + l2capoutbuf[25] = 0x03; + l2capoutbuf[26] = 0x19; + l2capoutbuf[27] = 0x11; + + l2capoutbuf[28] = 0x01; + l2capoutbuf[29] = 0x09; + l2capoutbuf[30] = 0x00; + l2capoutbuf[31] = 0x04; + l2capoutbuf[32] = 0x35; + l2capoutbuf[33] = 0x0C; + l2capoutbuf[34] = 0x35; + l2capoutbuf[35] = 0x03; + l2capoutbuf[36] = 0x19; + l2capoutbuf[37] = 0x01; + l2capoutbuf[38] = 0x00; + l2capoutbuf[39] = 0x35; + l2capoutbuf[40] = 0x05; + l2capoutbuf[41] = 0x19; + l2capoutbuf[42] = 0x00; + l2capoutbuf[43] = 0x03; + + l2capoutbuf[44] = 0x08; + l2capoutbuf[45] = 0x02; // Two more bytes? + l2capoutbuf[46] = 0x00; // 19 more bytes to come + l2capoutbuf[47] = 0x19; + + SDP_Command(l2capoutbuf,48); +} +void SPP::serialPortResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow) { + l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU; + l2capoutbuf[1] = transactionIDHigh; + l2capoutbuf[2] = transactionIDLow; + l2capoutbuf[3] = 0x00; // Parameter Length + l2capoutbuf[4] = 0x1C; // Parameter Length + l2capoutbuf[5] = 0x00; // AttributeListsByteCount + l2capoutbuf[6] = 0x19; // AttributeListsByteCount + + /* Attribute ID/Value Sequence: */ + l2capoutbuf[7] = 0x01; + l2capoutbuf[8] = 0x09; + l2capoutbuf[9] = 0x00; + l2capoutbuf[10] = 0x06; + l2capoutbuf[11] = 0x35; + + l2capoutbuf[12] = 0x09; + l2capoutbuf[13] = 0x09; + l2capoutbuf[14] = 0x65; + l2capoutbuf[15] = 0x6E; + l2capoutbuf[16] = 0x09; + l2capoutbuf[17] = 0x00; + l2capoutbuf[18] = 0x6A; + l2capoutbuf[19] = 0x09; + l2capoutbuf[20] = 0x01; + l2capoutbuf[21] = 0x00; + l2capoutbuf[22] = 0x09; + l2capoutbuf[23] = 0x01; + l2capoutbuf[24] = 0x00; + l2capoutbuf[25] = 0x25; + + l2capoutbuf[26] = 0x05; // Name length + l2capoutbuf[27] = 'T'; + l2capoutbuf[28] = 'K'; + l2capoutbuf[29] = 'J'; + l2capoutbuf[30] = 'S'; + l2capoutbuf[31] = 'P'; + l2capoutbuf[32] = 0x00; + + SDP_Command(l2capoutbuf,33); +} +void SPP::l2capResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow) { + serialPortResponse1(transactionIDHigh,transactionIDLow); // These has to send all the supported functions, since it only supports virtual serialport it just sends the message again +} +void SPP::l2capResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow) { + serialPortResponse2(transactionIDHigh,transactionIDLow); // Same data as serialPortResponse2 +} +/************************************************************/ +/* RFCOMM Commands */ +/************************************************************/ +void SPP::RFCOMM_Command(uint8_t* data, uint8_t nbytes) { + pBtd->L2CAP_Command(hci_handle,data,nbytes,rfcomm_scid[0],rfcomm_scid[1]); +} + +void SPP::sendRfcomm(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t* data, uint8_t length) { + l2capoutbuf[0] = channel | direction | CR | extendAddress; // RFCOMM Address + l2capoutbuf[1] = channelType | pfBit; // RFCOMM Control + l2capoutbuf[2] = length << 1 | 0x01; // Length and format (allways 0x01 bytes format) + uint8_t i = 0; + for(; i < length; i++) + l2capoutbuf[i+3] = data[i]; + l2capoutbuf[i+3] = calcFcs(l2capoutbuf); +#ifdef EXTRADEBUG + Notify(PSTR(" - RFCOMM Data: ")); + for(i = 0; i < length+4; i++) { + Serial.print(l2capoutbuf[i],HEX); + Notify(PSTR(" ")); + } +#endif + RFCOMM_Command(l2capoutbuf,length+4); +} + +void SPP::sendRfcommCredit(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t credit) { + l2capoutbuf[0] = channel | direction | CR | extendAddress; // RFCOMM Address + l2capoutbuf[1] = channelType | pfBit; // RFCOMM Control + l2capoutbuf[2] = 0x01; // Length = 0 + l2capoutbuf[3] = credit; // Credit + l2capoutbuf[4] = calcFcs(l2capoutbuf); +#ifdef EXTRADEBUG + Notify(PSTR(" - RFCOMM Credit Data: ")); + for(uint8_t i = 0; i < 5; i++) { + Serial.print(l2capoutbuf[i],HEX); + Notify(PSTR(" ")); + } +#endif + RFCOMM_Command(l2capoutbuf,5); +} + +/* CRC on 2 bytes */ +uint8_t SPP::__crc(uint8_t* data) { + return(pgm_read_byte(&rfcomm_crc_table[pgm_read_byte(&rfcomm_crc_table[0xff ^ data[0]]) ^ data[1]])); +} + +/* Calculate FCS - we never actually check if the host sends correct FCS to the Arduino */ +uint8_t SPP::calcFcs(uint8_t *data) { + if((data[1] & 0xEF) == RFCOMM_UIH) + return (0xff - __crc(data)); // FCS on 2 bytes + else + return (0xff - pgm_read_byte(&rfcomm_crc_table[__crc(data) ^ data[2]])); // FCS on 3 bytes +} + +/* Serial commands */ +void SPP::print(const char* data) { + l2capoutbuf[0] = rfcommChannelConnection | 0 | 0 | extendAddress;; // RFCOMM Address + l2capoutbuf[1] = RFCOMM_UIH; // RFCOMM Control + l2capoutbuf[2] = strlen(data) << 1 | 1; // Length + uint8_t i = 0; + for(; i < strlen(data); i++) + l2capoutbuf[i+3] = data[i]; + l2capoutbuf[i+3] = calcFcs(l2capoutbuf); + + RFCOMM_Command(l2capoutbuf,strlen(data)+4); +} +void SPP::print(uint8_t data) { + print(&data,1); +} +void SPP::print(uint8_t* array, uint8_t length) { + l2capoutbuf[0] = rfcommChannelConnection | 0 | 0 | extendAddress;; // RFCOMM Address + l2capoutbuf[1] = RFCOMM_UIH; // RFCOMM Control + l2capoutbuf[2] = length << 1 | 1; // Length + uint8_t i = 0; + for(; i < length; i++) + l2capoutbuf[i+3] = array[i]; + l2capoutbuf[i+3] = calcFcs(l2capoutbuf); + + RFCOMM_Command(l2capoutbuf,length+4); +} +void SPP::print(const __FlashStringHelper *ifsh) { + const char PROGMEM *p = (const char PROGMEM *)ifsh; + size_t size = 0; + while (1) { // Calculate the size of the string + uint8_t c = pgm_read_byte(p+size); + if (c == 0) + break; + size++; + } + uint8_t buf[size]; + + for(uint8_t i = 0; i < size; i++) + buf[i] = pgm_read_byte(p++); + + print(buf,size); +} + +void SPP::println(const char* data) { + char output[strlen(data)+2]; + strcpy(output,data); + strcat(output,"\r\n"); + print(output); +} +void SPP::println(uint8_t data) { + uint8_t buf[3] = {data, '\r', '\n'}; + print(buf,3); +} +void SPP::println(uint8_t* array, uint8_t length) { + uint8_t buf[length+2]; + memcpy(buf,array,length); + buf[length] = '\r'; + buf[length+1] = '\n'; + print(buf,length+2); +} +void SPP::println(const __FlashStringHelper *ifsh) { + const char PROGMEM *p = (const char PROGMEM *)ifsh; + size_t size = 0; + while (1) { // Calculate the size of the string + uint8_t c = pgm_read_byte(p+size); + if (c == 0) + break; + size++; + } + uint8_t buf[size+2]; + + for(uint8_t i = 0; i < size; i++) + buf[i] = pgm_read_byte(p++); + + buf[size] = '\r'; + buf[size+1] = '\n'; + print(buf,size+2); +} + +uint8_t SPP::read() { + uint8_t output = rfcommDataBuffer[0]; + for(uint8_t i = 1; i < rfcommAvailable; i++) + rfcommDataBuffer[i-1] = rfcommDataBuffer[i]; // Shift the buffer one left + rfcommAvailable--; + bytesRead++; + if(bytesRead > 250) { + bytesRead = 0; + sendRfcommCredit(rfcommChannelConnection,rfcommDirection,0,RFCOMM_UIH,0x10,0xFF); // Send 255 more credit +#ifdef EXTRADEBUG + Notify(PSTR("\r\nSent 255 more credit")); +#endif + } + return output; +} \ No newline at end of file diff --git a/SPP.h b/SPP.h new file mode 100644 index 00000000..6bf7d750 --- /dev/null +++ b/SPP.h @@ -0,0 +1,184 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + + This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#ifndef _spp_h_ +#define _spp_h_ + +#include "BTD.h" + +/* Bluetooth L2CAP states for SDP_task() */ +#define L2CAP_SDP_WAIT 0 +#define L2CAP_SDP_REQUEST 1 +#define L2CAP_SDP_SUCCESS 2 +#define L2CAP_SDP_DONE 3 +#define L2CAP_DISCONNECT_RESPONSE 4 + +/* Bluetooth L2CAP states for RFCOMM_task() */ +#define L2CAP_RFCOMM_WAIT 0 +#define L2CAP_RFCOMM_REQUEST 1 +#define L2CAP_RFCOMM_SUCCESS 2 +#define L2CAP_RFCOMM_DONE 3 + +/* L2CAP event flags */ +#define L2CAP_FLAG_CONNECTION_SDP_REQUEST 0x001 +#define L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST 0x002 +#define L2CAP_FLAG_CONFIG_SDP_REQUEST 0x004 +#define L2CAP_FLAG_CONFIG_RFCOMM_REQUEST 0x008 +#define L2CAP_FLAG_CONFIG_SDP_SUCCESS 0x010 +#define L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS 0x020 +#define L2CAP_FLAG_DISCONNECT_SDP_REQUEST 0x040 +#define L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST 0x080 +#define L2CAP_FLAG_DISCONNECT_RESPONSE 0x100 + +/* Macros for L2CAP event flag tests */ +#define l2cap_connection_request_sdp_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_SDP_REQUEST) +#define l2cap_connection_request_rfcomm_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST) +#define l2cap_config_request_sdp_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_SDP_REQUEST) +#define l2cap_config_request_rfcomm_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_RFCOMM_REQUEST) +#define l2cap_config_success_sdp_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_SDP_SUCCESS) +#define l2cap_config_success_rfcomm_flag (l2cap_event_flag & L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS) +#define l2cap_disconnect_request_sdp_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_SDP_REQUEST) +#define l2cap_disconnect_request_rfcomm_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST) +#define l2cap_disconnect_response_flag (l2cap_event_flag & L2CAP_FLAG_DISCONNECT_RESPONSE) + +/* Bluetooth L2CAP PSM */ +#define SDP_PSM 0x01 // Service Discovery Protocol PSM Value +#define RFCOMM_PSM 0x03 // RFCOMM PSM Value + +/* Used for SDP */ +#define SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU 0x06 // See the RFCOMM specs +#define SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU 0x07 // See the RFCOMM specs +#define SERIALPORT_UUID 0x1101 // See http://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm +#define L2CAP_UUID 0x0100 + +/* Used for RFCOMM */ +#define RFCOMM_SABM 0x2F +#define RFCOMM_UA 0x63 +#define RFCOMM_UIH 0xEF +//#define RFCOMM_DM 0x0F +#define RFCOMM_DISC 0x43 + +#define extendAddress 0x01 // Allways 1 + +// Multiplexer message types +#define BT_RFCOMM_PN_CMD 0x83 +#define BT_RFCOMM_PN_RSP 0x81 +#define BT_RFCOMM_MSC_CMD 0xE3 +#define BT_RFCOMM_MSC_RSP 0xE1 +#define BT_RFCOMM_RPN_CMD 0x93 +#define BT_RFCOMM_RPN_RSP 0x91 +/* +#define BT_RFCOMM_TEST_CMD 0x23 +#define BT_RFCOMM_TEST_RSP 0x21 +#define BT_RFCOMM_FCON_CMD 0xA3 +#define BT_RFCOMM_FCON_RSP 0xA1 +#define BT_RFCOMM_FCOFF_CMD 0x63 +#define BT_RFCOMM_FCOFF_RSP 0x61 +#define BT_RFCOMM_RLS_CMD 0x53 +#define BT_RFCOMM_RLS_RSP 0x51 +#define BT_RFCOMM_NSC_RSP 0x11 +*/ + +class SPP : public BluetoothService { +public: + SPP(BTD *p, const char* name = "Arduino", const char* pin = "1234"); + + // BluetoothService implementation + virtual void ACLData(uint8_t* ACLData); // Used to pass acldata to the services + virtual void Poll(); // Used to run SDP_task() and RFCOMM_task() + virtual void Release(); + virtual void disconnect(); // Used this void to disconnect the virtual serial port + + bool connected;// Variable used to indicate if the connection is established + + /* Serial port profile (SPP) commands */ + void print(const char* data); // Used to send strings + void print(uint8_t data); // Used to send single bytes + void print(uint8_t* array, uint8_t length); // Used to send arrays + void print(const __FlashStringHelper *); // Used to print strings stored in flash + + void println(const char* data); // Include newline and carriage return + void println(uint8_t data); // Include newline and carriage return + void println(uint8_t* array, uint8_t length); // Include newline and carriage return + void println(const __FlashStringHelper *); // Include newline and carriage return + + uint8_t available() { return rfcommAvailable; }; // Get the bytes waiting to be read + uint8_t read(); // Used to read the buffer + +private: + /* Bluetooth dongle library pointer */ + BTD *pBtd; + + /* Set true when a channel is created */ + bool SDPConnected; + bool RFCOMMConnected; + + uint16_t hci_handle; // The HCI Handle for the connection + + /* Variables used by L2CAP state maschines */ + uint8_t l2cap_sdp_state; + uint8_t l2cap_rfcomm_state; + uint16_t l2cap_event_flag; // l2cap flags of received bluetooth events + + uint8_t l2capoutbuf[BULK_MAXPKTSIZE]; // General purpose buffer for l2cap out data + uint8_t rfcommbuf[10]; // Buffer for RFCOMM Commands + + /* L2CAP Channels */ + uint8_t sdp_scid[2]; // L2CAP source CID for SDP + uint8_t sdp_dcid[2]; // 0x0050 + uint8_t rfcomm_scid[2]; // L2CAP source CID for RFCOMM + uint8_t rfcomm_dcid[2]; // 0x0051 + uint8_t identifier; // Identifier for command + + /* RFCOMM Variables */ + uint8_t rfcommChannel; + uint8_t rfcommChannelConnection; // This is the channel the SPP chanel will be running at + uint8_t rfcommDirection; + uint8_t rfcommCommandResponse; + uint8_t rfcommChannelType; + uint8_t rfcommPfBit; + + unsigned long timer; + bool waitForLastCommand; + bool creditSent; + + uint8_t rfcommDataBuffer[256]; // Create a 256 sized buffer for incoming data + uint8_t rfcommAvailable; + + bool firstMessage; // Used to see if it's the first SDP request received + uint8_t bytesRead; // Counter to see when it's time to send more credit + + /* State machines */ + void SDP_task(); // SDP state machine + void RFCOMM_task(); // RFCOMM state machine + + /* SDP Commands */ + void SDP_Command(uint8_t* data, uint8_t nbytes); + void serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow); + void serialPortResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow); + void serialPortResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow); + void l2capResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow); + void l2capResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow); + + /* RFCOMM Commands */ + void RFCOMM_Command(uint8_t* data, uint8_t nbytes); + void sendRfcomm(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t* data, uint8_t length); + void sendRfcommCredit(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t credit); + uint8_t calcFcs(uint8_t *data); + uint8_t __crc(uint8_t* data); +}; +#endif \ No newline at end of file