You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
MicroDexed/third-party/USBHost_t36/BluetoothConnection.cpp

2222 lines
92 KiB

/* USB EHCI Host for Teensy 3.6
* Copyright 2017 Paul Stoffregen (paul@pjrc.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* information about the BlueTooth HCI comes from logic analyzer captures
* plus... http://affon.narod.ru/BT/bluetooth_app_c10.pdf
*/
//=============================================================================
// Bluetooth connections code
//=============================================================================
#include <Arduino.h>
#include "USBHost_t36.h" // Read this header first for key info
#include "utility/bt_defines.h"
#define print USBHost::print_
#define println USBHost::println_//#define DEBUG_BT
//#define DEBUG_BT
//#define DEBUG_BT_VERBOSE
#ifndef DEBUG_BT
#undef DEBUG_BT_VERBOSE
void inline DBGPrintf(...) {};
#else
#define DBGPrintf USBHDBGSerial.printf
#endif
#ifndef DEBUG_BT_VERBOSE
void inline VDBGPrintf(...) {};
#else
#define VDBGPrintf USBHDBGSerial.printf
#endif
// This is a list of all the drivers inherited from the BTHIDInput class.
// Unlike the list of USBDriver (managed in enumeration.cpp), drivers stay
// on this list even when they have claimed a top level collection.
BluetoothConnection *BluetoothConnection::s_first_ = NULL;
// When a new top level collection is found, this function asks drivers
// if they wish to claim it. The driver taking ownership of the
// collection is returned, or NULL if no driver wants it.
//=============================================================================
// initialize the connection data
//=============================================================================
void BluetoothConnection::initializeConnection(BluetoothController *btController, uint8_t bdaddr[6], uint32_t class_of_device,
bool inquire_mode)
{
device_driver_ = nullptr;
btController_ = btController; // back pointer to main object
// lets setup a connection for this timer
bt_connection_timer_.init(btController_); // so it will use the main device
bt_connection_timer_.pointer = (void*)this; // but rememember us.
connection_rxid_ = 0;
control_dcid_ = 0x70;
interrupt_dcid_ = 0x71;
sdp_dcid_ = 0x40;
connection_complete_ = 0;
connection_started_ = false;
connection_started_by_timer_ = false;
//if (!connection_started_) {
// connection_started_ = true;
// btController_->setTimer(nullptr, 0); // clear out timer
//}
use_hid_protocol_ = false;
sdp_connected_ = false;
supports_SSP_ = false;
pending_control_tx_ = 0;
inquire_mode_ = inquire_mode;
find_driver_type_1_called_ = false; // bugbug should combine:
// We need to save away the BDADDR and class link type?
for (uint8_t i = 0; i < 6; i++) device_bdaddr_[i] = bdaddr[i];
device_class_ = class_of_device;
}
void BluetoothConnection::remoteNameComplete(const uint8_t *device_name)
{
// hack for now remember the device_name if we have one
if (device_name)strcpy((char*)remote_name_, (const char *)device_name);
else remote_name_[0] = 0; // null terminated string.
device_driver_ = find_driver(device_name, 0);
}
//=============================================================================
// Find a driver
//=============================================================================
BTHIDInput * BluetoothConnection::find_driver(const uint8_t *remoteName, int type)
{
if (type == 1) find_driver_type_1_called_ = true;
DBGPrintf("BluetoothController::find_driver(%x) type: %d\n", device_class_, type);
if (device_class_ & 0x2000) DBGPrintf(" (0x2000)Limited Discoverable Mode\n");
DBGPrintf(" (0x500)Peripheral device\n");
if (device_class_ & 0x80) DBGPrintf(" Mouse\n");
if (device_class_ & 0x40) DBGPrintf(" Keyboard\n");
switch (device_class_ & 0x3c) {
case 4: DBGPrintf(" Joystick\n"); break;
case 8: DBGPrintf(" Gamepad\n"); break;
case 0xc: DBGPrintf(" Remote Control\n"); break;
}
check_for_hid_descriptor_ = false;
BTHIDInput *driver = BluetoothController::available_bthid_drivers_list;
while (driver) {
DBGPrintf(" driver %x\n", (uint32_t)driver);
// should make const all the way through
hidclaim_t claim_type = driver->claim_bluetooth(this, device_class_, (uint8_t*)remoteName, type);
if (claim_type == CLAIM_INTERFACE) {
check_for_hid_descriptor_ = false;
DBGPrintf(" *** Claimed ***\n");
return driver;
} else if (claim_type == CLAIM_REPORT) {
DBGPrintf(" *** Check for HID ***\n");
check_for_hid_descriptor_ = true;
}
driver = driver->next;
}
return NULL;
}
//=============================================================================
// Handle the rx2_
//=============================================================================
void BluetoothConnection::rx2_data(uint8_t *rx2buf) // called from rx2_data of BluetoothController
{
// need to detect if these are L2CAP or SDP or ...
uint16_t data_len = rx2buf[4] + ((uint16_t)rx2buf[5] << 8);
uint16_t dcid = rx2buf[6] + ((uint16_t)rx2buf[7] << 8);
//DBGPrintf("@@@@@@ SDP MSG? %x %x %x @@@@@", dcid, sdp_dcid_, rx2buf[8]);
if (dcid == sdp_dcid_) {
switch (rx2buf[8]) {
case SDP_SERVICE_SEARCH_REQUEST:
process_sdp_service_search_request(&rx2buf[8]);
break;
case SDP_SERVICE_SEARCH_RESPONSE:
process_sdp_service_search_response(&rx2buf[8]);
break;
case SDP_SERVICE_ATTRIBUTE_REQUEST:
process_sdp_service_attribute_request(&rx2buf[8]);
break;
case SDP_SERVICE_ATTRIBUTE_RESPONSE:
process_sdp_service_attribute_response(&rx2buf[8]);
break;
case SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST:
process_sdp_service_search_attribute_request(&rx2buf[8]);
break;
case SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE:
process_sdp_service_search_attribute_response(&rx2buf[8]);
break;
}
} else {
switch (rx2buf[8]) {
case L2CAP_CMD_CONNECTION_REQUEST:
process_l2cap_connection_request(&rx2buf[8], data_len);
break;
case L2CAP_CMD_CONNECTION_RESPONSE:
process_l2cap_connection_response(&rx2buf[8], data_len);
break;
case L2CAP_CMD_CONFIG_REQUEST:
process_l2cap_config_request(&rx2buf[8], data_len);
break;
case L2CAP_CMD_CONFIG_RESPONSE:
process_l2cap_config_response(&rx2buf[8], data_len);
break;
case HID_THDR_DATA_INPUT:
handleHIDTHDRData(rx2buf); // Pass the whole buffer...
break;
case L2CAP_CMD_COMMAND_REJECT:
process_l2cap_command_reject(&rx2buf[8], data_len);
break;
case L2CAP_CMD_DISCONNECT_REQUEST:
process_l2cap_disconnect_request(&rx2buf[8], data_len);
break;
}
}
}
//=============================================================================
// Process tx_data and state
//=============================================================================
void BluetoothConnection::tx_data(uint8_t *data, uint16_t length)
{
#ifdef DEBUG_BT_VERBOSE
DBGPrintf("tx_data callback (bluetooth): %d : ", pending_control_tx_);
for (uint8_t i = 0; i < length; i++) DBGPrintf("%02x ", data[i]);
DBGPrintf("\n");
#endif
switch (pending_control_tx_) {
case STATE_TX_SEND_CONNECT_INT:
delay(1);
connection_rxid_++;
sendl2cap_ConnectionRequest(device_connection_handle_, connection_rxid_, interrupt_dcid_, HID_INTR_PSM);
pending_control_tx_ = 0;
break;
case STATE_TX_SEND_CONECT_RSP_SUCCESS:
delay(1);
// Tell the device we are ready
sendl2cap_ConnectionResponse(device_connection_handle_, connection_rxid_++, control_dcid_, control_scid_, SUCCESSFUL);
pending_control_tx_ = STATE_TX_SEND_CONFIG_REQ;
break;
case STATE_TX_SEND_CONFIG_REQ:
delay(1);
sendl2cap_ConfigRequest(device_connection_handle_, connection_rxid_, control_scid_);
pending_control_tx_ = 0;
break;
case STATE_TX_SEND_CONECT_ISR_RSP_SUCCESS:
delay(1);
// Tell the device we are ready
sendl2cap_ConnectionResponse(device_connection_handle_, connection_rxid_++, interrupt_dcid_, interrupt_scid_, SUCCESSFUL);
pending_control_tx_ = STATE_TX_SEND_CONFIG_ISR_REQ;
break;
case STATE_TX_SEND_CONFIG_ISR_REQ:
delay(1);
sendl2cap_ConfigRequest(device_connection_handle_, connection_rxid_, interrupt_scid_);
pending_control_tx_ = 0;
break;
case STATE_TX_SEND_CONECT_SDP_RSP_SUCCESS:
delay(1);
sendl2cap_ConnectionResponse(device_connection_handle_, connection_rxid_, sdp_dcid_, sdp_scid_, SUCCESSFUL);
pending_control_tx_ = STATE_TX_SEND_CONFIG_SDP_REQ;
break;
case STATE_TX_SEND_CONFIG_SDP_REQ:
delay(1);
sendl2cap_ConfigRequest(device_connection_handle_, connection_rxid_, sdp_scid_);
pending_control_tx_ = 0;
break;
}
}
//=============================================================================
// Moved handling of L2CAP and HID messages
//=============================================================================
// Experiment to see if we can get SDP connectin setup
void BluetoothConnection::connectToSDP() {
DBGPrintf("$$$ BluetoothController::connectToSDP() Called\n");
connection_rxid_++;
sendl2cap_ConnectionRequest(device_connection_handle_, connection_rxid_,
sdp_dcid_, SDP_PSM);
pending_control_tx_ = 0;
}
void BluetoothConnection::process_l2cap_connection_request(uint8_t *data, uint16_t length) {
// ID LEN LEN PSM PSM SCID SCID
// 0x02 0x02 0x04 0x00 0x11 0x00 0x43 0x00
uint16_t psm = data[4] + ((uint16_t)data[5] << 8);
uint16_t scid = data[6] + ((uint16_t)data[7] << 8);
connection_started_ = true;
btController_->setTimer(nullptr, 0); // clear out timer
connection_rxid_ = data[1];
DBGPrintf(" recv L2CAP Connection Request: ID: %d, PSM: %x, SCID: %x\n", connection_rxid_, psm, scid);
// Assuming not pair mode Send response like:
// RXID Len LEN DCID DCID SCID SCID RES 0 0 0
// 0x03 0x02 0x08 0x00 0x70 0x00 0x43 0x00 0x01 0x00 0x00 0x00
if (psm == HID_CTRL_PSM) {
control_scid_ = scid;
sendl2cap_ConnectionResponse(device_connection_handle_, connection_rxid_, control_dcid_, control_scid_, PENDING);
pending_control_tx_ = STATE_TX_SEND_CONECT_RSP_SUCCESS;
} else if (psm == HID_INTR_PSM) {
interrupt_scid_ = scid;
sendl2cap_ConnectionResponse(device_connection_handle_, connection_rxid_, interrupt_dcid_, interrupt_scid_, PENDING);
pending_control_tx_ = STATE_TX_SEND_CONECT_ISR_RSP_SUCCESS;
} else if (psm == SDP_PSM) {
DBGPrintf(" <<< SDP PSM >>>\n");
sdp_scid_ = scid;
sendl2cap_ConnectionResponse(device_connection_handle_, connection_rxid_, sdp_dcid_, sdp_scid_, PENDING);
pending_control_tx_ = STATE_TX_SEND_CONECT_SDP_RSP_SUCCESS;
}
}
// Process the l2cap_connection_response...
void BluetoothConnection::process_l2cap_connection_response(uint8_t *data, uint16_t length) {
uint16_t scid = data[4] + ((uint16_t)data[5] << 8);
uint16_t dcid = data[6] + ((uint16_t)data[7] << 8);
uint16_t result = data[8] + ((uint16_t)data[9] << 8);
DBGPrintf(" recv L2CAP Connection Response: ID: %d, Dest:%x, Source:%x, Result:%x, Status: %x pending control: %x %x\n",
data[1], scid, dcid,
result, data[10] + ((uint16_t)data[11] << 8), btController_->pending_control_, pending_control_tx_);
// Experiment ignore if: pending_control_tx_ = STATE_TX_SEND_CONFIG_REQ;
if ((pending_control_tx_ == STATE_TX_SEND_CONFIG_REQ) || (pending_control_tx_ == STATE_TX_SEND_CONECT_RSP_SUCCESS)) {
DBGPrintf(" *** State == STATE_TX_SEND_CONFIG_REQ - try ignoring\n");
return;
}
//48 20 10 0 | c 0 1 0 | 3 0 8 0 44 0 70 0 0 0 0 0
if (dcid == interrupt_dcid_) {
interrupt_scid_ = scid;
DBGPrintf(" Interrupt Response\n");
connection_rxid_++;
sendl2cap_ConfigRequest(device_connection_handle_, connection_rxid_, scid);
} else if (dcid == control_dcid_) {
control_scid_ = scid;
DBGPrintf(" Control Response\n");
sendl2cap_ConfigRequest(device_connection_handle_, connection_rxid_, scid);
} else if (dcid == sdp_dcid_) {
// Check for failure!
DBGPrintf(" SDP Response\n");
if (result != 0) {
DBGPrintf(" Failed - No SDP!\n");
// Enable SCan to page mode
sdp_connected_ = false;
connection_complete_ |= CCON_SDP;
if (connection_complete_ == CCON_ALL)
btController_->sendHCIWriteScanEnable(2);
} else {
sdp_scid_ = scid;
sendl2cap_ConfigRequest(device_connection_handle_, connection_rxid_, scid);
}
} else {
DBGPrintf(" Unknown Response\n");
// Unknown dcid... Guess
if (((connection_complete_ | CCON_SDP) == CCON_ALL) && (result !=0)) {
DBGPrintf(" Guess SDP Response failure\n");
connection_complete_ |= CCON_SDP;
btController_->sendHCIWriteScanEnable(2);
}
}
}
void BluetoothConnection::process_l2cap_config_request(uint8_t *data, uint16_t length) {
//48 20 10 00 0c 00 01 00 * 04 02 08 00 70 00 00 00 01 02 30 00
//48 20 10 00 0C 00 01 00 * 04 03 08 00 70 00 00 00 01 02 48 00
uint16_t dcid = data[4] + ((uint16_t)data[5] << 8);
DBGPrintf(" recv L2CAP config Request: ID: %d, Dest:%x, Flags:%x, Options:",
data[1], dcid, data[6] + ((uint16_t)data[7] << 8));
for (uint16_t i = 8; i < length; i++) DBGPrintf(" %02x", data[i]);
uint16_t mtu = 0x2A0; // hard coded what we were returning....
for (uint16_t i = 8; i < length; i++) {
if ((data[i] == 1) && (data[i+1] == 2)) {
mtu = data[i+2] + (data[i+3] << 8);
DBGPrintf(" MTU:%u", mtu);
}
i += data[i+1] + 2;
}
DBGPrintf("\n");
// Now see which dest was specified
if (dcid == control_dcid_) {
DBGPrintf(" Control Configuration request\n");
sendl2cap_ConfigResponse(device_connection_handle_, data[1], control_scid_, mtu);
connection_complete_ |= CCON_CONT;
} else if (dcid == interrupt_dcid_) {
DBGPrintf(" Interrupt Configuration request\n");
sendl2cap_ConfigResponse(device_connection_handle_, data[1], interrupt_scid_, mtu);
connection_complete_ |= CCON_INT;
} else if (dcid == sdp_dcid_) {
DBGPrintf(" SDP Configuration request\n");
sendl2cap_ConfigResponse(device_connection_handle_, data[1], sdp_scid_, mtu);
connection_complete_ |= CCON_SDP;
sdp_connected_ = true;
} else if (dcid == sdp_scid_) {
DBGPrintf(" SDP Configuration request (But our SCID?)\n");
// maybe we should change the ids around?
sendl2cap_ConfigResponse(device_connection_handle_, data[1], sdp_scid_, mtu);
// We see this with some PS4?
// Enable SCan to page mode
connection_complete_ |= CCON_SDP;
sdp_connected_ = true;
}
}
void BluetoothConnection::process_l2cap_config_response(uint8_t *data, uint16_t length) {
// 48 20 12 0 e 0 1 0 5 0 a 0 70 0 0 0 0 0 1 2 30 0
uint16_t scid = data[4] + ((uint16_t)data[5] << 8);
uint16_t result = data[8] + ((uint16_t)data[9] << 8);
DBGPrintf(" recv L2CAP config Response: ID: %d, Source:%x, Flags:%x, Result:%x",
data[1], scid, data[6] + ((uint16_t)data[7] << 8), result );
switch (result) {
case 0x0000: DBGPrintf("(Success)"); break;
case 0x0001: DBGPrintf("(Failure – unacceptable parameters)"); break;
case 0x0002: DBGPrintf("(Failure – rejected (no reason provided))"); break;
case 0x0003: DBGPrintf("(Failure – unknown options)"); break;
case 0x0004: DBGPrintf("(Pending)"); break;
case 0x0005: DBGPrintf("(Failure - flow spec rejected)"); break;
}
if (length > 10) {
DBGPrintf(", Config: ");
for (uint16_t i = 10; i < length; i++) DBGPrintf(" %02x", data[i]);
for (uint16_t i = 10; i < length; i++) {
if ((data[i] == 1) && (data[i+1] == 2)) {
uint16_t mtu = data[i+2] + (data[i+3] << 8);
DBGPrintf(" MTU:%u", mtu);
}
i += data[i+1] + 2;
}
}
DBGPrintf("\n");
if (scid == control_dcid_) {
// Maybe we should avoid setting HID mode and only do
// so if the class of device is either/or MOUSE or Keyboard.
// Set HID Boot mode
// Don't do if PS3... Or if class told us not to
if (device_class_ & 0xC0) {
if (use_hid_protocol_) {
// see what happens if I tell it to
btController_->setHIDProtocol(HID_RPT_PROTOCOL);
} else {
// don't call through Null pointer
if ((device_driver_ == nullptr) ||
!(device_driver_->special_process_required & BTHIDInput::SP_PS3_IDS)) {
btController_->setHIDProtocol(HID_BOOT_PROTOCOL); //
}
}
}
//setHIDProtocol(HID_RPT_PROTOCOL); //HID_RPT_PROTOCOL
if ((btController_->do_pair_device_ || btController_->do_pair_ssp_) && !(device_driver_ && (device_driver_->special_process_required & BTHIDInput::SP_DONT_NEED_CONNECT))) {
pending_control_tx_ = STATE_TX_SEND_CONNECT_INT;
} else if (device_driver_ && (device_driver_->special_process_required & BTHIDInput::SP_NEED_CONNECT)) {
DBGPrintf(" Needs connect to device INT(PS4?)\n");
// The PS4 requires a connection request to it.
pending_control_tx_ = STATE_TX_SEND_CONNECT_INT;
} else if (connection_started_by_timer_) {
DBGPrintf(" Connection started by timeout continue to interrupt\n");
// The PS4 requires a connection request to it.
pending_control_tx_ = STATE_TX_SEND_CONNECT_INT;
} else {
btController_->pending_control_ = 0;
}
// Could be cleaner
if (((device_class_ & 0xC0) == 0) && (pending_control_tx_ == STATE_TX_SEND_CONNECT_INT)) {
// if we are not mouse and keybboard and we need to send the connect int
// do it now.
delay(1);
connection_rxid_++;
sendl2cap_ConnectionRequest(device_connection_handle_, connection_rxid_, interrupt_dcid_, HID_INTR_PSM);
pending_control_tx_ = 0;
}
DBGPrintf("\tNew Pending control pair:%u driver:%p HID:%u TX: %x\n", btController_->do_pair_device_, device_driver_, check_for_hid_descriptor_, pending_control_tx_);
connection_complete_ |= CCON_CONT;
} else if (scid == interrupt_dcid_) {
// Lets try SDP connect if we are not already connected.
if (connection_started_by_timer_) {
DBGPrintf(" Connection started by timeout experiment continue to SDP\n");
} else
if (!check_for_hid_descriptor_) connection_complete_ |= CCON_SDP; // Don't force connect if no is asking for HID
if ((connection_complete_ & CCON_SDP) == 0) connectToSDP(); // temp to see if we can do this later...
// Enable SCan to page mode
//connection_complete_ = true;
//sendHCIWriteScanEnable(2);
connection_complete_ |= CCON_INT;
} else if (scid == sdp_dcid_) {
// Enable SCan to page mode
DBGPrintf(" SDP configuration complete\n");
// Enable SCan to page mode
connection_complete_ |= CCON_SDP;
sdp_connected_ = true;
}
if (connection_complete_ == CCON_ALL) {
btController_->sendHCIWriteScanEnable(2);
}
}
void BluetoothConnection::process_l2cap_command_reject(uint8_t *data, uint16_t length) {
// 48 20 b 0 7 0 70 0 *1 0 0 0 2 0 4
DBGPrintf(" recv L2CAP command reject: ID: %d, length:%x, Reason:%x, Data: %x %x \n",
data[1], data[2] + ((uint16_t)data[3] << 8), data[4], data[5], data[6]);
}
void BluetoothConnection::process_l2cap_disconnect_request(uint8_t *data, uint16_t length) {
uint16_t dcid = data[4] + ((uint16_t)data[5] << 8);
uint16_t scid = data[6] + ((uint16_t)data[7] << 8);
DBGPrintf(" recv L2CAP disconnect request: ID: %d, Length:%x, Dest:%x, Source:%x\n",
data[1], data[2] + ((uint16_t)data[3] << 8), dcid, scid);
// May need to respond in some cases...
if (dcid == control_dcid_) {
DBGPrintf(" Control disconnect request\n");
} else if (dcid == interrupt_dcid_) {
DBGPrintf(" Interrupt disconnect request\n");
} else if (dcid == sdp_dcid_) {
DBGPrintf(" SDP disconnect request\n");
sdp_connected_ = false; // say we are not connected
sendl2cap_DisconnectResponse(device_connection_handle_, data[1],
sdp_dcid_,
sdp_scid_);
}
}
void BluetoothConnection::handleHIDTHDRData(uint8_t *data) {
// Example
// T HID data
//48 20 d 0 9 0 71 0 a1 3 8a cc c5 a 23 22 79
uint16_t len = data[4] + ((uint16_t)data[5] << 8);
DBGPrintf("HID HDR Data: len: %d, Type: %d Con:%p\n", len, data[9], this);
// ??? How to parse??? Use HID object???
#if 0
if (!find_driver_type_1_called_ && !device_driver_ && !have_hid_descriptor_) {
// If we got to here and don't have driver or ... try once to get one
DBGPrintf("\t $$$ No HID or Driver: See if one wants it now\n");
// BUGBUG we initialize descriptor with name...
device_driver_ = find_driver(descriptor_, 1);
}
#endif
if (device_driver_) {
device_driver_->process_bluetooth_HID_data(&data[9], len - 1); // We skip the first byte...
} else if (have_hid_descriptor_) {
// nead to bias to location within data.
parse(0x0100 | data[9], &data[10], len - 2);
} else {
switch (data[9]) {
case 1:
DBGPrintf(" Keyboard report type\n");
break;
case 2:
DBGPrintf(" Mouse report type\n");
break;
case 3:
DBGPrintf(" Combo keyboard/pointing\n");
break;
default:
DBGPrintf(" Unknown report\n");
}
}
}
void BluetoothConnection::handle_HCI_WRITE_SCAN_ENABLE_complete(uint8_t *rxbuf)
{
// See if we have driver and a remote
DBGPrintf("Write_Scan_enable Completed - connection state: %x\n", connection_complete_);
if (connection_complete_ == CCON_ALL) { // We have a driver call their
if (device_driver_) {
device_driver_->connectionComplete();
} else if (check_for_hid_descriptor_) {
have_hid_descriptor_ = false;
sdp_buffer_ = descriptor_;
sdp_buffer_len_ = sizeof(descriptor_);
if (!startSDP_ServiceSearchAttributeRequest(0x206, 0x206, sdp_buffer_, sdp_buffer_len_)) {
// Maybe try to claim_driver as we won't get a HID report.
DBGPrintf("Failed to start SDP attribute request - so lets try again to find a driver");
device_driver_ = find_driver(descriptor_, 1);
}
}
connection_complete_ = 0; // only call once
}
}
void BluetoothConnection::handle_HCI_OP_ROLE_DISCOVERY_complete(uint8_t *rxbuf)
{
// PS4 looks something like: 0E 07 01 09 08 00 47 00 01
// Others look like : 0E 07 01 09 08 00 48 00 00
// last byte is the interesting one says what role.
DBGPrintf("ROLE_DISCOVERY Completed - Role: %x ", rxbuf[8]);
if (rxbuf[8] == 0) {
DBGPrintf("(Central)\n");
// Set a timeout
btController_->setTimer(this, CONNECTION_TIMEOUT_US);
} else {
DBGPrintf("(Peripheral)\n");
// Should maybe do some state checking before doing this, but...
DBGPrintf("\t** We will issue connection requsts **\n ");
connection_started_ = true;
sendl2cap_ConnectionRequest(device_connection_handle_, connection_rxid_, control_dcid_, HID_CTRL_PSM);
}
}
void BluetoothConnection::timer_event(USBDriverTimer *whichTimer)
{
DBGPrintf("BluetoothConnection::timer_event(%p) : %u\n", this, connection_started_);
if (whichTimer == &bt_connection_timer_) {
if (device_driver_) {
device_driver_->bt_hid_timer_event(whichTimer);
}
} else if (!connection_started_) {
DBGPrintf("\t** Timed out now try issue connection requsts **\n ");
connection_started_ = true;
connection_started_by_timer_ = true;
connection_rxid_++;
sendl2cap_ConnectionRequest(device_connection_handle_, connection_rxid_, control_dcid_, HID_CTRL_PSM);
}
}
// l2cap support functions.
void BluetoothConnection::sendl2cap_ConnectionResponse(uint16_t handle, uint8_t rxid, uint16_t dcid, uint16_t scid, uint8_t result) {
uint8_t l2capbuf[12];
l2capbuf[0] = L2CAP_CMD_CONNECTION_RESPONSE; // Code
l2capbuf[1] = rxid; // Identifier
l2capbuf[2] = 0x08; // Length
l2capbuf[3] = 0x00;
l2capbuf[4] = dcid & 0xff; // Destination CID
l2capbuf[5] = dcid >> 8;
l2capbuf[6] = scid & 0xff; // Source CID
l2capbuf[7] = scid >> 8;
l2capbuf[8] = result; // Result: Pending or Success
l2capbuf[9] = 0x00;
l2capbuf[10] = 0x00; // No further information
l2capbuf[11] = 0x00;
DBGPrintf("\nSend L2CAP_CMD_CONNECTION_RESPONSE(RXID:%x, DCID:%x, SCID:%x RES:%x)\n", rxid, dcid, scid, result);
btController_->sendL2CapCommand(handle, l2capbuf, sizeof(l2capbuf));
}
void BluetoothConnection::sendl2cap_ConnectionRequest(uint16_t handle, uint8_t rxid, uint16_t scid, uint16_t psm) {
uint8_t l2capbuf[8];
l2capbuf[0] = L2CAP_CMD_CONNECTION_REQUEST; // Code
l2capbuf[1] = rxid; // Identifier
l2capbuf[2] = 0x04; // Length
l2capbuf[3] = 0x00;
l2capbuf[4] = (uint8_t)(psm & 0xff); // PSM
l2capbuf[5] = (uint8_t)(psm >> 8);
l2capbuf[6] = scid & 0xff; // Source CID
l2capbuf[7] = (scid >> 8) & 0xff;
DBGPrintf("\nSend ConnectionRequest (RXID:%x, SCID:%x, PSM:%x)\n", rxid, scid, psm);
btController_->sendL2CapCommand(handle, l2capbuf, sizeof(l2capbuf));
}
void BluetoothConnection::sendl2cap_ConfigRequest(uint16_t handle, uint8_t rxid, uint16_t dcid) {
uint8_t l2capbuf[12];
l2capbuf[0] = L2CAP_CMD_CONFIG_REQUEST; // Code
l2capbuf[1] = rxid; // Identifier
l2capbuf[2] = 0x08; // Length
l2capbuf[3] = 0x00;
l2capbuf[4] = dcid & 0xff; // Destination CID
l2capbuf[5] = (dcid >> 8) & 0xff;
l2capbuf[6] = 0x00; // Flags
l2capbuf[7] = 0x00;
l2capbuf[8] = 0x01; // Config Opt: type = MTU (Maximum Transmission Unit) - Hint
l2capbuf[9] = 0x02; // Config Opt: length
l2capbuf[10] = 0xFF; // MTU
l2capbuf[11] = 0xFF;
DBGPrintf("\nsend L2CAP_ConfigRequest(RXID:%x, DCID:%x)\n", rxid, dcid);
btController_->sendL2CapCommand(handle, l2capbuf, sizeof(l2capbuf));
}
void BluetoothConnection::sendl2cap_ConfigResponse(uint16_t handle, uint8_t rxid, uint16_t scid, uint16_t mtu) {
uint8_t l2capbuf[10];
l2capbuf[0] = L2CAP_CMD_CONFIG_RESPONSE; // Code
l2capbuf[1] = rxid; // Identifier
l2capbuf[2] = 0x06; // try not sending mtu 0x0A; // Length
l2capbuf[3] = 0x00;
l2capbuf[4] = scid & 0xff; // Source CID
l2capbuf[5] = (scid >> 8) & 0xff;
l2capbuf[6] = 0x00; // Flag
l2capbuf[7] = 0x00;
l2capbuf[8] = 0x00; // Result
l2capbuf[9] = 0x00;
//l2capbuf[10] = 0x01; // Config
//l2capbuf[11] = 0x02;
//l2capbuf[12] = mtu & 0xff; //0xA0;
//l2capbuf[13] = mtu >> 8; //0x02;
DBGPrintf("\nsend L2CAP_ConfigResponse(RXID:%x, SCID:%x)\n", rxid, scid);
btController_->sendL2CapCommand(handle, l2capbuf, sizeof(l2capbuf));
}
void BluetoothConnection::sendl2cap_DisconnectResponse(uint16_t handle, uint8_t rxid, uint16_t dcid, uint16_t scid) {
uint8_t l2capbuf[8];
l2capbuf[0] = L2CAP_CMD_DISCONNECT_RESPONSE; // Code
l2capbuf[1] = rxid; // Identifier
l2capbuf[2] = 0x04; // Length
l2capbuf[3] = 0x00;
l2capbuf[4] = dcid & 0xff; // dcid CID
l2capbuf[5] = (dcid >> 8) & 0xff;
l2capbuf[6] = scid & 0xff; // SCID CID
l2capbuf[7] = (scid >> 8) & 0xff;
DBGPrintf("\nsend L2CAP_DisconnectResponse(RXID:%x, DCID:%x, SCID:%x)\n", rxid, dcid, scid);
btController_->sendL2CapCommand(handle, l2capbuf, sizeof(l2capbuf));
}
//=============================================================================
// Process the SDP stuff.
//=============================================================================
bool BluetoothConnection::startSDP_ServiceSearchAttributeRequest(uint16_t range_low, uint16_t range_high, uint8_t *buffer, uint32_t cb)
{
if (!sdp_connected_) return false;
sdp_request_range_low_ = range_low;
sdp_reqest_range_high_ = range_high;
sdp_request_buffer_ = buffer;
sdp_request_buffer_cb_ = cb;
sdp_request_buffer_used_cnt_ = 0; // cnt in bytes used.
sdp_request_completed_ = false;
// So start it up
send_SDP_ServiceSearchAttributeRequest(nullptr, 0);
return true;
}
void BluetoothConnection::send_SDP_ServiceSearchRequest(uint8_t *continue_state, uint8_t cb)
{
// Example message: first part l2cap. Which will let them fill that in
// l2cap: 0x47 0x0 0x18 0x0 0x14 0x0 0x42 0x0
// PSM header: 0x6 0x0 0x0 0x0 0xf
// req: 0x35 0x3 0x19 0x1 0x0 0xff 0xff 0x35 0x5 0xa 0x0 0x0 0xff 0xff 0x0
uint8_t sdpcmdbuf[30]; // 20 + up to 10 continue
seq_number_++;
uint16_t packet_size = (13 - 5) + cb;
sdpcmdbuf[0] = 0x2; // SDP_ServiceSearchRequest
sdpcmdbuf[1] = seq_number_ >> 8; // Sequence number
sdpcmdbuf[2] = seq_number_ & 0xff; //
sdpcmdbuf[3] = packet_size >> 8; // Data in the rest...
sdpcmdbuf[4] = packet_size & 0xff; //
// Now lets build the attribute request data.
// 0x35 0x3 0x19 0x1 0x0 0xff 0xff 0x35 0x5 0xa 0x0 0x0 0xff 0xff 0x2 0x0 0x26
sdpcmdbuf[5] = 0x35; // type sequence
sdpcmdbuf[6] = 0x03; // 3 bytes
sdpcmdbuf[7] = 0x19; // UUID 2 bytes
sdpcmdbuf[8] = 0x01; // UUID
sdpcmdbuf[9] = 0x00; // UUID
sdpcmdbuf[10] = 0xff; // MAX attributes
sdpcmdbuf[11] = 0xff; //
sdpcmdbuf[12] = cb; // Count of continuation bytes
for (uint8_t i = 0; i < cb; i++) sdpcmdbuf[13 + i] = continue_state[i];
// Now lets try to send the packet
btController_->sendL2CapCommand(sdpcmdbuf, 13 + cb, sdp_scid_);
}
void BluetoothConnection::send_SDP_ServiceSearchAttributeRequest(uint8_t *continue_state, uint8_t cb)
{
// Example message: first part l2cap. Which will let them fill that in
// l2cap: 0x47 0x0 0x18 0x0 0x14 0x0 0x42 0x0
// PSM header: 0x6 0x0 0x0 0x0 0xf
// req: 0x35 0x3 0x19 0x1 0x0 0xff 0xff 0x35 0x5 0xa 0x0 0x0 0xff 0xff 0x0
uint16_t continue_data_offset = 17;
uint8_t sdpcmdbuf[37]; // 20 + up to 17 continue
seq_number_++;
sdpcmdbuf[0] = 0x6; // SDP_ServiceSearchAttributeReques
sdpcmdbuf[1] = seq_number_ >> 8; // Sequence number
sdpcmdbuf[2] = seq_number_ & 0xff; //
//sdpcmdbuf[3] = packet_size >> 8; // Data in the rest...
//sdpcmdbuf[4] = packet_size & 0xff; //
// Now lets build the attribute request data.
// 0x35 0x3 0x19 0x1 0x0 0xff 0xff 0x35 0x5 0xa 0x0 0x0 0xff 0xff 0x2 0x0 0x26
sdpcmdbuf[5] = 0x35; // type sequence
sdpcmdbuf[6] = 0x03; // 3 bytes
sdpcmdbuf[7] = 0x19; // UUID 2 bytes
sdpcmdbuf[8] = 0x01; // UUID
sdpcmdbuf[9] = 0x00; // UUID
sdpcmdbuf[10] = 0xff; // MAX size
sdpcmdbuf[11] = 0xff; //
sdpcmdbuf[12] = 0x35; // Sequence
if (sdp_request_range_low_ == sdp_reqest_range_high_) {
// doing specific
sdpcmdbuf[13] = 0x03; // 3 bytes
sdpcmdbuf[14] = 0x09; // 2 byte integer
sdpcmdbuf[15] = sdp_request_range_low_ >> 8; // Attribute low
sdpcmdbuf[16] = sdp_request_range_low_ & 0xff; //
} else {
// doing range
sdpcmdbuf[13] = 0x05; // 5 bytes
sdpcmdbuf[14] = 0x0A; // 4 byte integer
sdpcmdbuf[15] = sdp_request_range_low_ >> 8; // Attribute low
sdpcmdbuf[16] = sdp_request_range_low_ & 0xff; //
sdpcmdbuf[17] = sdp_reqest_range_high_ & 0xff; // high
sdpcmdbuf[18] = sdp_reqest_range_high_ & 0xff; // high
continue_data_offset = 19;
}
sdpcmdbuf[continue_data_offset++] = cb; // Count of continuation bytes
for (uint8_t i = 0; i < cb; i++) sdpcmdbuf[continue_data_offset + i] = continue_state[i];
uint16_t packet_size = (continue_data_offset - 5) + cb;
sdpcmdbuf[3] = packet_size >> 8; // Data in the rest...
sdpcmdbuf[4] = packet_size & 0xff; //
// Now lets try to send the packet
btController_->sendL2CapCommand(sdpcmdbuf, continue_data_offset + cb, sdp_scid_);
}
//----------------------------------------------------------------
// Some SDP stuff.
void BluetoothConnection::process_sdp_service_search_request(uint8_t *data) {
DBGPrintf("process_sdp_service_search_request\n");
}
void BluetoothConnection::process_sdp_service_search_response(uint8_t *data) {
DBGPrintf("process_sdp_service_search_response\n");
// (processed before us)(0) 48 20 1A 00 16 00 40 00
// (0) 03 00 01 00 11
// (5) 00 03 00 03 00 00 00 00 00 01 00 00 00 01 00 01 00
uint16_t total_count = (data[5] << 8) + data[6];
uint16_t current_count = (data[7] << 8) + data[8];
uint8_t offset = 9;
DBGPrintf("\tTotal Count:%u Current:%u", total_count, current_count);
for (uint8_t i = 0; i < current_count; i++) {
uint32_t srh = ((uint32_t)data[offset + 0] << 24) + ((uint32_t)data[offset + 1] << 16) +
((uint32_t)data[offset + 2] << 8) + (data[offset + 3]);
DBGPrintf(" %08X", srh);
offset += 4;
}
uint8_t continue_state_count = data[offset++];
DBGPrintf(" Cont CB:%u", continue_state_count);
for (uint8_t i = 0; i < continue_state_count; i++) DBGPrintf(" %02X", data[offset++]);
DBGPrintf("\n");
}
void BluetoothConnection::process_sdp_service_attribute_request(uint8_t *data) {
DBGPrintf("process_sdp_service_attribute_request\n");
}
void BluetoothConnection::process_sdp_service_attribute_response(uint8_t *data) {
DBGPrintf("process_sdp_service_attribute_response:\n");
}
void BluetoothConnection::process_sdp_service_search_attribute_request(uint8_t *data) {
DBGPrintf("\n### process_sdp_service_search_attribute_request ###\n");
// Print out the data like UHS2
#ifdef DEBUG_BT
uint16_t service = data[1] << 8 | data[2];
uint16_t length = data[3] << 8 | data[4];
uint16_t uuid = (data[8] << 8 | data[9]);
if (uuid == 0x0000) // Check if it's sending the UUID as a 128-bit UUID
uuid = (data[10] << 8 | data[11]);
DBGPrintf(" Service:%x UUID:%x Length:%u\n", service, uuid, length);
DBGPrintf(" Data:");
for (uint8_t i = 0; i < length; i++) {
DBGPrintf("%02x ", data[5 + i]);
}
DBGPrintf("\n");
#endif
// lets respond saying we don't support the service
uint8_t l2capbuf[10];
l2capbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE;
l2capbuf[1] = data[1];
l2capbuf[2] = data[2];
l2capbuf[3] = 0x00; // MSB Parameter Length
l2capbuf[4] = 0x05; // LSB Parameter Length = 5
l2capbuf[5] = 0x00; // MSB AttributeListsByteCount
l2capbuf[6] = 0x02; // LSB AttributeListsByteCount = 2
/* Attribute ID/Value Sequence: */
l2capbuf[7] = 0x35; // Data element sequence - length in next byte
l2capbuf[8] = 0x00; // Length = 0
l2capbuf[9] = 0x00; // No continuation state
DBGPrintf("Send SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE not supported\n");
btController_->sendL2CapCommand(device_connection_handle_, l2capbuf, sizeof(l2capbuf),
sdp_scid_ & 0xff, sdp_scid_ >> 8);
}
void BluetoothConnection::process_sdp_service_search_attribute_response(uint8_t *data) {
//before :0B 20 34 00 30 00 40 00
// (00) 07 00 01 00 2B
// (5) 00 26 // cb_data
// (7) 36 03 A2 36 00 8E 09 00 00 0A 00 00 00 00 09 00 01 35 03 19 10 00 09 00 04 35 0D 35 06 19 01 00 09 00 01 35 03 19 // data
// (7 + cb_data) 02 00 26 //
uint16_t cb_data = (data[5] << 8) + data[6];
uint8_t cb_cont = data[7 + cb_data];
uint8_t *pb_cont = &data[8 + cb_data];
DBGPrintf("process_sdp_service_search_attribute_response: cb dat:%u cont:%u ", cb_data, cb_cont);
for (uint8_t i = 0; i < cb_cont; i++) DBGPrintf(" %02X", pb_cont[i]);
DBGPrintf("\n");
// lets copy the data in...
uint16_t cb_space = sdp_request_buffer_cb_ - sdp_request_buffer_used_cnt_;
if (cb_data > cb_space) cb_data = cb_space;
memcpy(&sdp_request_buffer_[sdp_request_buffer_used_cnt_], &data[7], cb_data);
sdp_request_buffer_used_cnt_ += cb_data;
// Now see if we are done or not, if not start up next request
if ((cb_cont == 0) || (sdp_request_buffer_used_cnt_ == sdp_request_buffer_cb_)) {
sdp_request_completed_ = true;
if (device_driver_) {
device_driver_->sdp_command_completed(true); // We skip the first byte...
} else {
// Must be our own... we should now try to proces it.
completeSDPRequest(true);
}
} else {
send_SDP_ServiceSearchAttributeRequest(pb_cont, cb_cont);
}
}
//=============================================================================
// HID SUPPORT - maybe split out
//=============================================================================
// lets use the SDP Support functions to try to retrieve the
// HID Descriptor using SDP
//=============================================================================
bool BluetoothConnection::completeSDPRequest(bool success)
{
if (!success) return false;
// Now real hack:
// Lets see if we can now print out the report descriptor.
uint32_t cb_left = sdp_request_buffer_used_cnt_;
uint8_t *pb = sdp_buffer_; // start at second byte;
sdp_element_t sdpe;
bool found_report_attribute = false;
while (cb_left > 0) {
int cb = extract_next_SDP_Token(pb, cb_left, sdpe);
if (cb <= 0 ) break;
// Should do a lot more validation, but ...
// At least check that we see a value with our attribute number
if ((sdpe.dtype == DU32) && (sdpe.data.uw == 0x206)) found_report_attribute = true;
if (found_report_attribute && (sdpe.element_type == 4) && (sdpe.dtype == DPB)) {
descsize_ = sdpe.element_size;
memcpy(descriptor_, sdpe.data.pb, descsize_);
dumpHIDReportDescriptor();
have_hid_descriptor_ = true;
parse();
return true;
}
cb_left -= cb;
pb += cb;
}
return false;
}
//BUGBUG: move to class or ...
int BluetoothConnection::extract_next_SDP_Token(uint8_t *pbElement, int cb_left, sdp_element_t &sdpe) {
uint8_t element = *pbElement; // first byte is type of element
sdpe.element_type = element >> 3;
sdpe.element_size = element & 7;
sdpe.data.luw = 0; // start off 0
int size_of_element = -1; // should never use this initialized value
switch (sdpe.element_size) {
case 0: size_of_element = 2; break;
case 1: size_of_element = 3; break;
case 2: size_of_element = 5; break;
case 3: size_of_element = 9; break;
case 4: size_of_element = 17; break;
case 5: size_of_element = pbElement[1] + 2; break;
case 6: size_of_element = (pbElement[1] << 8) + pbElement[2] + 3; break;
case 7: size_of_element = (uint32_t)(pbElement[1] << 24) + (uint32_t)(pbElement[2] << 16) + (pbElement[3] << 8) + pbElement[4] + 5; break;
}
DBGPrintf("extract_next_SDP_Token %p %x %u %u ", pbElement , element, sdpe.element_type, sdpe.element_size);
switch (element) {
case 0: // nil
sdpe.dtype = DNIL;
VDBGPrintf("(NIL)");
break;
case 0x08: // unsigned one byte
case 0x18: // UUID one byte
case 0x28: // bool one byte
sdpe.dtype = DU32;
sdpe.data.uw = pbElement[1];
VDBGPrintf("(DU32 %u)", sdpe.data.uw);
break;
case 0x09: // unsigned 2 byte
case 0x19: // uuid 2 byte
sdpe.dtype = DU32;
sdpe.data.uw = (pbElement[1] << 8) + pbElement[2];
VDBGPrintf("(DU32 %u)", sdpe.data.uw);
break;
case 0x0A: // unsigned 4 byte
case 0x1A: // UUID 4 byte
sdpe.dtype = DU32;
sdpe.data.uw = (uint32_t)(pbElement[1] << 24) + (uint32_t)(pbElement[2] << 16) + (pbElement[3] << 8) + pbElement[4];
VDBGPrintf("(DU32 %u)", sdpe.data.uw);
break;
case 0x0B: // unsigned 8 byte
sdpe.dtype = DU64;
sdpe.data.luw = ((uint64_t)pbElement[1] << 52) + ((uint64_t)pbElement[2] << 48) + ((uint64_t)pbElement[3] << 40) + ((uint64_t)pbElement[4] << 32) +
(uint32_t)(pbElement[5] << 24) + (uint32_t)(pbElement[6] << 16) + (pbElement[7] << 8) + pbElement[8];
VDBGPrintf("(DU64 %llu)", sdpe.data.luw);
break;
// type = 2 signed
case 0x10: // unsigned one byte
sdpe.dtype = DS32;
sdpe.data.sw = (int8_t)pbElement[1];
VDBGPrintf("(DS32 %u)", sdpe.data.uw);
break;
case 0x11: // unsigned 2 byte
sdpe.dtype = DS32;
sdpe.data.sw = (int16_t)((pbElement[1] << 8) + pbElement[2]);
break;
case 0x12: // unsigned 4 byte
sdpe.dtype = DS32;
sdpe.data.sw = (int32_t)((uint32_t)(pbElement[1] << 24) + (uint32_t)(pbElement[2] << 16) + (pbElement[3] << 8) + pbElement[4]);
VDBGPrintf("(DS32 %u)", sdpe.data.uw);
break;
case 0x13: //
sdpe.dtype = DS64;
sdpe.data.lsw = (int64_t)(((uint64_t)pbElement[1] << 52) + ((uint64_t)pbElement[2] << 48) + ((uint64_t)pbElement[3] << 40) + ((uint64_t)pbElement[4] << 32) +
(uint32_t)(pbElement[5] << 24) + (uint32_t)(pbElement[6] << 16) + (pbElement[7] << 8) + pbElement[8]);
VDBGPrintf("(DS32 %u)", sdpe.data.uw);
break;
// string one byte size.
case 0x25:
sdpe.dtype = DPB;
sdpe.element_size = pbElement[1];
sdpe.data.pb = &pbElement[2];
VDBGPrintf("(DPB %02x %02x %02x)", sdpe.data.pb[0], sdpe.data.pb[1], sdpe.data.pb[2]);
break;
case 0x26:
sdpe.dtype = DPB;
sdpe.element_size = (pbElement[1] << 8) + pbElement[2];
sdpe.data.pb = &pbElement[3];
VDBGPrintf("(DPB %02x %02x %02x)", sdpe.data.pb[0], sdpe.data.pb[1], sdpe.data.pb[2]);
break;
// type = 7 Data element sequence
case 0x35: //
case 0x3D: //
sdpe.dtype = DLVL;
sdpe.element_size = pbElement[1];
sdpe.data.pb = &pbElement[2];
size_of_element = 2; // we don't advance through hole thing only header
VDBGPrintf("(DLVL)");
break;
case 0x36: //
case 0x3E: //
sdpe.dtype = DLVL;
sdpe.element_size = (pbElement[1] << 8) + pbElement[2];
sdpe.data.pb = &pbElement[3];
size_of_element = 3; // we don't advance through hole thing only header
VDBGPrintf("(DLVL)");
break;
case 0x37: //
case 0x3F: //
sdpe.dtype = DLVL;
sdpe.element_size = (uint32_t)(pbElement[1] << 24) + (uint32_t)(pbElement[2] << 16) + (pbElement[3] << 8) + pbElement[4];
sdpe.data.pb = &pbElement[5];
size_of_element = 5; // we don't advance through hole thing only header
VDBGPrintf("(DLVL)");
break;
default:
sdpe.dtype = DUNKOWN;
DBGPrintf("### DECODE failed %x type:%u size:%u cb:%u ###\n", element, sdpe.element_type, sdpe.element_size, size_of_element);
break;
}
DBGPrintf(" %u %u\n", sdpe.dtype, size_of_element);
return size_of_element;
}
void BluetoothConnection::dumpHIDReportDescriptor() {
#ifdef DEBUG_BT_VERBOSE
uint8_t *pb = descriptor_;
uint16_t cb = descsize_;
const uint8_t *p = pb;
uint16_t report_size = cb;
const uint8_t *pend = p + report_size;
uint8_t collection_level = 0;
uint16_t usage_page = 0;
enum { USAGE_LIST_LEN = 24 };
uint16_t usage[USAGE_LIST_LEN] = { 0, 0 };
uint8_t usage_count = 0;
uint32_t topusage;
//cnt_feature_reports_ = 0;
//uint8_t last_report_id = 0;
DBGPrintf("\nHID Report Descriptor (%p) size: %u\n", p, report_size);
while (p < pend) {
uint8_t tag = *p;
for (uint8_t i = 0; i < collection_level; i++) DBGPrintf(" ");
DBGPrintf(" %02X", tag);
if (tag == 0xFE) { // Long Item (unsupported)
p += p[1] + 3;
continue;
}
uint32_t val;
switch (tag & 0x03) { // Short Item data
case 0:
val = 0;
p++;
break;
case 1:
val = p[1];
// could be better;
DBGPrintf(" %02X", p[1]);
p += 2;
break;
case 2:
val = p[1] | (p[2] << 8);
DBGPrintf(" %02X %02X", p[1], p[2]);
p += 3;
break;
case 3:
val = p[1] | (p[2] << 8) | (p[3] << 16) | (p[4] << 24);
DBGPrintf(" %02X %02X %02X %02X", p[1], p[2], p[3], p[4]);
p += 5;
break;
}
if (p > pend) break;
bool reset_local = false;
switch (tag & 0xfc) {
case 0x4: //usage Page
{
usage_page = val;
DBGPrintf("\t// Usage Page(%x) - ", val);
switch (usage_page) {
case 0x01: DBGPrintf("Generic Desktop"); break;
case 0x06: DBGPrintf("Generic Device Controls"); break;
case 0x07: DBGPrintf("Keycode"); break;
case 0x08: DBGPrintf("LEDs"); break;
case 0x09: DBGPrintf("Button"); break;
case 0x0C: DBGPrintf("Consumer"); break;
case 0x0D:
case 0xFF0D: DBGPrintf("Digitizer"); break;
default:
if (usage_page >= 0xFF00) DBGPrintf("Vendor Defined");
else DBGPrintf("Other ?");
break;
}
}
break;
case 0x08: //usage
DBGPrintf("\t// Usage(%x) -", val);
printUsageInfo(usage_page, val);
if (usage_count < USAGE_LIST_LEN) {
// Usages: 0 is reserved 0x1-0x1f is sort of reserved for top level things like
// 0x1 - Pointer - A collection... So lets try ignoring these
if (val > 0x1f) {
usage[usage_count++] = val;
}
}
break;
case 0x14: // Logical Minimum (global)
DBGPrintf("\t// Logical Minimum(%x)", val);
break;
case 0x24: // Logical Maximum (global)
DBGPrintf("\t// Logical maximum(%x)", val);
break;
case 0x74: // Report Size (global)
DBGPrintf("\t// Report Size(%x)", val);
break;
case 0x94: // Report Count (global)
DBGPrintf("\t// Report Count(%x)", val);
break;
case 0x84: // Report ID (global)
DBGPrintf("\t// Report ID(%x)", val);
//last_report_id = val;
break;
case 0x18: // Usage Minimum (local)
usage[0] = val;
usage_count = 255;
DBGPrintf("\t// Usage Minimum(%x) - ", val);
printUsageInfo(usage_page, val);
break;
case 0x28: // Usage Maximum (local)
usage[1] = val;
usage_count = 255;
DBGPrintf("\t// Usage Maximum(%x) - ", val);
printUsageInfo(usage_page, val);
break;
case 0xA0: // Collection
DBGPrintf("\t// Collection(%x)", val);
// discard collection info if not top level, hopefully that's ok?
if (collection_level == 0) {
topusage = ((uint32_t)usage_page << 16) | usage[0];
DBGPrintf(" top Usage(%x)", topusage);
collection_level++;
}
reset_local = true;
break;
case 0xC0: // End Collection
DBGPrintf("\t// End Collection");
if (collection_level > 0) collection_level--;
break;
case 0x80: // Input
DBGPrintf("\t// Input(%x)\t// (", val);
print_input_output_feature_bits(val);
reset_local = true;
break;
case 0x90: // Output
DBGPrintf("\t// Output(%x)\t// (", val);
print_input_output_feature_bits(val);
reset_local = true;
break;
case 0xB0: // Feature
DBGPrintf("\t// Feature(%x)\t// (", val);
print_input_output_feature_bits(val);
//if (cnt_feature_reports_ < MAX_FEATURE_REPORTS) {
// feature_report_ids_[cnt_feature_reports_++] = last_report_id;
//}
reset_local = true;
break;
case 0x34: // Physical Minimum (global)
DBGPrintf("\t// Physical Minimum(%x)", val);
break;
case 0x44: // Physical Maximum (global)
DBGPrintf("\t// Physical Maximum(%x)", val);
break;
case 0x54: // Unit Exponent (global)
DBGPrintf("\t// Unit Exponent(%x)", val);
break;
case 0x64: // Unit (global)
DBGPrintf("\t// Unit(%x)", val);
break;
}
if (reset_local) {
usage_count = 0;
usage[0] = 0;
usage[1] = 0;
}
DBGPrintf("\n");
}
#endif
}
#ifdef DEBUG_BT_VERBOSE
void BluetoothConnection::print_input_output_feature_bits(uint8_t val) {
DBGPrintf((val & 0x01) ? "Constant" : "Data");
DBGPrintf((val & 0x02) ? ", Variable" : ", Array");
DBGPrintf((val & 0x04) ? ", Relative" : ", Absolute");
if (val & 0x08) DBGPrintf(", Wrap");
if (val & 0x10) DBGPrintf(", Non Linear");
if (val & 0x20) DBGPrintf(", No Preferred");
if (val & 0x40) DBGPrintf(", Null State");
if (val & 0x80) DBGPrintf(", Volatile");
if (val & 0x100) DBGPrintf(", Buffered Bytes");
DBGPrintf(")");
}
void BluetoothConnection::printUsageInfo(uint8_t usage_page, uint16_t usage) {
switch (usage_page) {
case 1: // Generic Desktop control:
switch (usage) {
case 0x01: DBGPrintf("(Pointer)"); break;
case 0x02: DBGPrintf("(Mouse)"); break;
case 0x04: DBGPrintf("(Joystick)"); break;
case 0x05: DBGPrintf("(Gamepad)"); break;
case 0x06: DBGPrintf("(Keyboard)"); break;
case 0x07: DBGPrintf("(Keypad)"); break;
case 0x30: DBGPrintf("(X)"); break;
case 0x31: DBGPrintf("(Y)"); break;
case 0x32: DBGPrintf("(Z)"); break;
case 0x33: DBGPrintf("(Rx)"); break;
case 0x34: DBGPrintf("(Ry)"); break;
case 0x35: DBGPrintf("(Rz)"); break;
case 0x36: DBGPrintf("(Slider)"); break;
case 0x37: DBGPrintf("(Dial)"); break;
case 0x38: DBGPrintf("(Wheel)"); break;
case 0x39: DBGPrintf("(Hat)"); break;
case 0x3D: DBGPrintf("(Start)"); break;
case 0x3E: DBGPrintf("(Select)"); break;
case 0x40: DBGPrintf("(Vx)"); break;
case 0x41: DBGPrintf("(Vy)"); break;
case 0x42: DBGPrintf("(Vz)"); break;
case 0x43: DBGPrintf("(Vbrx)"); break;
case 0x44: DBGPrintf("(Vbry)"); break;
case 0x45: DBGPrintf("(Vbrz)"); break;
case 0x46: DBGPrintf("(Vno)"); break;
case 0x81: DBGPrintf("(System Power Down)"); break;
case 0x82: DBGPrintf("(System Sleep)"); break;
case 0x83: DBGPrintf("(System Wake Up)"); break;
case 0x90: DBGPrintf("(D-Up)"); break;
case 0x91: DBGPrintf("(D-Dn)"); break;
case 0x92: DBGPrintf("(D-Right)"); break;
case 0x93: DBGPrintf("(D-Left)"); break;
default:
DBGPrintf("(?)");
break;
}
break;
case 6: // Generic Desktop control:
switch (usage) {
case 0x020: DBGPrintf("(Battery Strength)"); break;
case 0x21: DBGPrintf("(Wireless Channel)"); break;
case 0x22: DBGPrintf("(Wireless ID)"); break;
case 0x23: DBGPrintf("(Discover Wireless Ctrl)"); break;
case 0x24: DBGPrintf("(Security Code Entered)"); break;
case 0x25: DBGPrintf("(Security Code erase)"); break;
case 0x26: DBGPrintf("(Security Code cleared)");
default: DBGPrintf("(?)"); break;
}
break;
case 7: // keyboard/keycode
switch (usage) {
case 0x04: DBGPrintf("(a and A)"); break;
case 0x05: DBGPrintf("(b and B)"); break;
case 0x06: DBGPrintf("(c and C)"); break;
case 0x07: DBGPrintf("(d and D)"); break;
case 0x08: DBGPrintf("(e and E)"); break;
case 0x09: DBGPrintf("(f and F)"); break;
case 0x0A: DBGPrintf("(g and G)"); break;
case 0x0B: DBGPrintf("(h and H)"); break;
case 0x0C: DBGPrintf("(i and I)"); break;
case 0x0D: DBGPrintf("(j and J)"); break;
case 0x0E: DBGPrintf("(k and K)"); break;
case 0x0F: DBGPrintf("(l and L)"); break;
case 0x10: DBGPrintf("(m and M)"); break;
case 0x11: DBGPrintf("(n and N)"); break;
case 0x12: DBGPrintf("(o and O)"); break;
case 0x13: DBGPrintf("(p and P)"); break;
case 0x14: DBGPrintf("(q and Q)"); break;
case 0x15: DBGPrintf("(r and R)"); break;
case 0x16: DBGPrintf("(s and S)"); break;
case 0x17: DBGPrintf("(t and T)"); break;
case 0x18: DBGPrintf("(u and U)"); break;
case 0x19: DBGPrintf("(v and V)"); break;
case 0x1A: DBGPrintf("(w and W)"); break;
case 0x1B: DBGPrintf("(x and X)"); break;
case 0x1C: DBGPrintf("(y and Y)"); break;
case 0x1D: DBGPrintf("(z and Z)"); break;
case 0x1E: DBGPrintf("(1 and !)"); break;
case 0x1F: DBGPrintf("(2 and @)"); break;
case 0x20: DBGPrintf("(3 and #)"); break;
case 0x21: DBGPrintf("(4 and $)"); break;
case 0x22: DBGPrintf("(5 and %)"); break;
case 0x23: DBGPrintf("(6 and ^)"); break;
case 0x24: DBGPrintf("(7 and &)"); break;
case 0x25: DBGPrintf("(8 and *)"); break;
case 0x26: DBGPrintf("(9 and ()"); break;
case 0x27: DBGPrintf("(0 and ))"); break;
case 0x28: DBGPrintf("(Return (ENTER))"); break;
case 0x29: DBGPrintf("(ESCAPE)"); break;
case 0x2A: DBGPrintf("(DELETE (Backspace))"); break;
case 0x2B: DBGPrintf("(Tab)"); break;
case 0x2C: DBGPrintf("(Spacebar)"); break;
case 0x2D: DBGPrintf("(- and (underscore))"); break;
case 0x2E: DBGPrintf("(= and +)"); break;
case 0x2F: DBGPrintf("([ and {)"); break;
case 0x30: DBGPrintf("(] and })"); break;
case 0x31: DBGPrintf("(\and |)"); break;
case 0x32: DBGPrintf("(Non-US # and ˜)"); break;
case 0x33: DBGPrintf("(; and :)"); break;
case 0x34: DBGPrintf("(‘ and “)"); break;
case 0x35: DBGPrintf("(Grave Accent and Tilde)"); break;
case 0x36: DBGPrintf("(, and <)"); break;
case 0x37: DBGPrintf("(. and >)"); break;
case 0x38: DBGPrintf("(/ and ?)"); break;
case 0x39: DBGPrintf("(Caps Lock)"); break;
case 0x3A: DBGPrintf("(F1)"); break;
case 0x3B: DBGPrintf("(F2)"); break;
case 0x3C: DBGPrintf("(F3)"); break;
case 0x3D: DBGPrintf("(F4)"); break;
case 0x3E: DBGPrintf("(F5)"); break;
case 0x3F: DBGPrintf("(F6)"); break;
case 0x40: DBGPrintf("(F7)"); break;
case 0x41: DBGPrintf("(F8)"); break;
case 0x42: DBGPrintf("(F9)"); break;
case 0x43: DBGPrintf("(F10)"); break;
case 0x44: DBGPrintf("(F11)"); break;
case 0x45: DBGPrintf("(F12)"); break;
case 0x46: DBGPrintf("(PrintScreen)"); break;
case 0x47: DBGPrintf("(Scroll Lock)"); break;
case 0x48: DBGPrintf("(Pause)"); break;
case 0x49: DBGPrintf("(Insert)"); break;
case 0x4A: DBGPrintf("(Home)"); break;
case 0x4B: DBGPrintf("(PageUp)"); break;
case 0x4C: DBGPrintf("(Delete Forward)"); break;
case 0x4D: DBGPrintf("(End)"); break;
case 0x4E: DBGPrintf("(PageDown)"); break;
case 0x4F: DBGPrintf("(RightArrow)"); break;
case 0x50: DBGPrintf("(LeftArrow)"); break;
case 0x51: DBGPrintf("(DownArrow)"); break;
case 0x52: DBGPrintf("(UpArrow)"); break;
case 0x53: DBGPrintf("(Keypad Num Lock and Clear)"); break;
case 0x54: DBGPrintf("(Keypad /)"); break;
case 0x55: DBGPrintf("(Keypad *)"); break;
case 0x56: DBGPrintf("(Keypad -)"); break;
case 0x57: DBGPrintf("(Keypad +)"); break;
case 0x58: DBGPrintf("(Keypad ENTER)"); break;
case 0x59: DBGPrintf("(Keypad 1 and End)"); break;
case 0x5A: DBGPrintf("(Keypad 2 and Down Arrow)"); break;
case 0x5B: DBGPrintf("(Keypad 3 and PageDn)"); break;
case 0x5C: DBGPrintf("(Keypad 4 and Left Arrow)"); break;
case 0x5D: DBGPrintf("(Keypad 5)"); break;
case 0x5E: DBGPrintf("(Keypad 6 and Right Arrow)"); break;
case 0x5F: DBGPrintf("(Keypad 7 and Home)"); break;
case 0x60: DBGPrintf("(Keypad 8 and Up Arrow)"); break;
case 0x61: DBGPrintf("(Keypad 9 and PageUp)"); break;
case 0x62: DBGPrintf("(Keypad 0 and Insert)"); break;
case 0x63: DBGPrintf("(Keypad . and Delete)"); break;
case 0x64: DBGPrintf("(Non-US \and |)"); break;
case 0x65: DBGPrintf("(Application)"); break;
case 0x66: DBGPrintf("(Power)"); break;
case 0x67: DBGPrintf("(Keypad =)"); break;
case 0x68: DBGPrintf("(F13)"); break;
case 0x69: DBGPrintf("(F14)"); break;
case 0x6A: DBGPrintf("(F15)"); break;
case 0x6B: DBGPrintf("(F16)"); break;
case 0x6C: DBGPrintf("(F17)"); break;
case 0x6D: DBGPrintf("(F18)"); break;
case 0x6E: DBGPrintf("(F19)"); break;
case 0x6F: DBGPrintf("(F20)"); break;
case 0x70: DBGPrintf("(F21)"); break;
case 0x71: DBGPrintf("(F22)"); break;
case 0x72: DBGPrintf("(F23)"); break;
case 0x73: DBGPrintf("(F24)"); break;
case 0x74: DBGPrintf("(Execute)"); break;
case 0x75: DBGPrintf("(Help)"); break;
case 0x76: DBGPrintf("(Menu)"); break;
case 0x77: DBGPrintf("(Select)"); break;
case 0x78: DBGPrintf("(Stop)"); break;
case 0x79: DBGPrintf("(Again)"); break;
case 0x7A: DBGPrintf("(Undo)"); break;
case 0x7B: DBGPrintf("(Cut)"); break;
case 0x7C: DBGPrintf("(Copy)"); break;
case 0x7D: DBGPrintf("(Paste)"); break;
case 0x7E: DBGPrintf("(Find)"); break;
case 0x7F: DBGPrintf("(Mute)"); break;
case 0x80: DBGPrintf("(Volume Up)"); break;
case 0x81: DBGPrintf("(Volume Down)"); break;
case 0x82: DBGPrintf("(Locking Caps Lock)"); break;
case 0x83: DBGPrintf("(Locking Num Lock)"); break;
case 0x84: DBGPrintf("(Locking Scroll Lock)"); break;
case 0x85: DBGPrintf("(Keypad Comma)"); break;
case 0x99: DBGPrintf("(Alternate Erase)"); break;
case 0x9A: DBGPrintf("(SysReq/Attention)"); break;
case 0x9B: DBGPrintf("(Cancel)"); break;
case 0x9C: DBGPrintf("(Clear)"); break;
case 0x9D: DBGPrintf("(Prior)"); break;
case 0x9E: DBGPrintf("(Return)"); break;
case 0x9F: DBGPrintf("(Separator)"); break;
case 0xA0: DBGPrintf("(Out)"); break;
case 0xA1: DBGPrintf("(Oper)"); break;
case 0xA2: DBGPrintf("(Clear/Again)"); break;
case 0xA3: DBGPrintf("(CrSel/Props)"); break;
case 0xA4: DBGPrintf("(ExSel)"); break;
case 0xA5: DBGPrintf("(AF Reserved)"); break;
case 0xB0: DBGPrintf("(Keypad 00)"); break;
case 0xB1: DBGPrintf("(Keypad 000)"); break;
case 0xB2: DBGPrintf("(Thousands Separator)"); break;
case 0xB3: DBGPrintf("(Decimal Separator)"); break;
case 0xB4: DBGPrintf("(Currency Unit)"); break;
case 0xB5: DBGPrintf("(Currency Sub-unit)"); break;
case 0xB6: DBGPrintf("(Keypad ()"); break;
case 0xB7: DBGPrintf("(Keypad ))"); break;
case 0xB8: DBGPrintf("(Keypad {)"); break;
case 0xB9: DBGPrintf("(Keypad })"); break;
case 0xBA: DBGPrintf("(Keypad Tab)"); break;
case 0xBB: DBGPrintf("(Keypad Backspace)"); break;
case 0xBC: DBGPrintf("(Keypad A)"); break;
case 0xBD: DBGPrintf("(Keypad B)"); break;
case 0xBE: DBGPrintf("(Keypad C)"); break;
case 0xBF: DBGPrintf("(Keypad D)"); break;
case 0xC0: DBGPrintf("(Keypad E)"); break;
case 0xC1: DBGPrintf("(Keypad F)"); break;
case 0xC2: DBGPrintf("(Keypad XOR)"); break;
case 0xC3: DBGPrintf("(Keypad ^)"); break;
case 0xC4: DBGPrintf("(Keypad %)"); break;
case 0xC5: DBGPrintf("(Keypad <)"); break;
case 0xC6: DBGPrintf("(Keypad >)"); break;
case 0xC7: DBGPrintf("(Keypad &)"); break;
case 0xC8: DBGPrintf("(Keypad &&)"); break;
case 0xC9: DBGPrintf("(Keypad |)"); break;
case 0xCA: DBGPrintf("(Keypad ||)"); break;
case 0xCB: DBGPrintf("(Keypad :)"); break;
case 0xCC: DBGPrintf("(Keypad #)"); break;
case 0xCD: DBGPrintf("(Keypad Space)"); break;
case 0xCE: DBGPrintf("(Keypad @)"); break;
case 0xCF: DBGPrintf("(Keypad !)"); break;
case 0xD0: DBGPrintf("(Keypad Memory Store)"); break;
case 0xD1: DBGPrintf("(Keypad Memory Recall)"); break;
case 0xD2: DBGPrintf("(Keypad Memory Clear)"); break;
case 0xD3: DBGPrintf("(Keypad Memory Add)"); break;
case 0xD4: DBGPrintf("(Keypad Memory Subtract)"); break;
case 0xD5: DBGPrintf("(Keypad Memory Multiply)"); break;
case 0xD6: DBGPrintf("(Keypad Memory Divide)"); break;
case 0xD7: DBGPrintf("(Keypad +/-)"); break;
case 0xD8: DBGPrintf("(Keypad Clear)"); break;
case 0xD9: DBGPrintf("(Keypad Clear Entry)"); break;
case 0xDA: DBGPrintf("(Keypad Binary)"); break;
case 0xDB: DBGPrintf("(Keypad Octal)"); break;
case 0xDC: DBGPrintf("(Keypad Decimal)"); break;
case 0xDD: DBGPrintf("(Keypad Hexadecimal)"); break;
case 0xE0: DBGPrintf("(Left Control)"); break;
case 0xE1: DBGPrintf("(Left Shift)"); break;
case 0xE2: DBGPrintf("(Left Alt)"); break;
case 0xE3: DBGPrintf("(Left GUI)"); break;
case 0xE4: DBGPrintf("(Right Control)"); break;
case 0xE5: DBGPrintf("(Right Shift)"); break;
case 0xE6: DBGPrintf("(Right Alt)"); break;
case 0xE7: DBGPrintf("(Right GUI)"); break;
default:
DBGPrintf("(Keycode %u)", usage);
break;
}
break;
case 9: // Button
DBGPrintf(" (BUTTON %d)", usage);
break;
case 0xC: // Consummer page
switch (usage) {
case 0x01: DBGPrintf("(Consumer Controls)"); break;
case 0x20: DBGPrintf("(+10)"); break;
case 0x21: DBGPrintf("(+100)"); break;
case 0x22: DBGPrintf("(AM/PM)"); break;
case 0x30: DBGPrintf("(Power)"); break;
case 0x31: DBGPrintf("(Reset)"); break;
case 0x32: DBGPrintf("(Sleep)"); break;
case 0x33: DBGPrintf("(Sleep After)"); break;
case 0x34: DBGPrintf("(Sleep Mode)"); break;
case 0x35: DBGPrintf("(Illumination)"); break;
case 0x36: DBGPrintf("(Function Buttons)"); break;
case 0x40: DBGPrintf("(Menu)"); break;
case 0x41: DBGPrintf("(Menu Pick)"); break;
case 0x42: DBGPrintf("(Menu Up)"); break;
case 0x43: DBGPrintf("(Menu Down)"); break;
case 0x44: DBGPrintf("(Menu Left)"); break;
case 0x45: DBGPrintf("(Menu Right)"); break;
case 0x46: DBGPrintf("(Menu Escape)"); break;
case 0x47: DBGPrintf("(Menu Value Increase)"); break;
case 0x48: DBGPrintf("(Menu Value Decrease)"); break;
case 0x60: DBGPrintf("(Data On Screen)"); break;
case 0x61: DBGPrintf("(Closed Caption)"); break;
case 0x62: DBGPrintf("(Closed Caption Select)"); break;
case 0x63: DBGPrintf("(VCR/TV)"); break;
case 0x64: DBGPrintf("(Broadcast Mode)"); break;
case 0x65: DBGPrintf("(Snapshot)"); break;
case 0x66: DBGPrintf("(Still)"); break;
case 0x80: DBGPrintf("(Selection)"); break;
case 0x81: DBGPrintf("(Assign Selection)"); break;
case 0x82: DBGPrintf("(Mode Step)"); break;
case 0x83: DBGPrintf("(Recall Last)"); break;
case 0x84: DBGPrintf("(Enter Channel)"); break;
case 0x85: DBGPrintf("(Order Movie)"); break;
case 0x86: DBGPrintf("(Channel)"); break;
case 0x87: DBGPrintf("(Media Selection)"); break;
case 0x88: DBGPrintf("(Media Select Computer)"); break;
case 0x89: DBGPrintf("(Media Select TV)"); break;
case 0x8A: DBGPrintf("(Media Select WWW)"); break;
case 0x8B: DBGPrintf("(Media Select DVD)"); break;
case 0x8C: DBGPrintf("(Media Select Telephone)"); break;
case 0x8D: DBGPrintf("(Media Select Program Guide)"); break;
case 0x8E: DBGPrintf("(Media Select Video Phone)"); break;
case 0x8F: DBGPrintf("(Media Select Games)"); break;
case 0x90: DBGPrintf("(Media Select Messages)"); break;
case 0x91: DBGPrintf("(Media Select CD)"); break;
case 0x92: DBGPrintf("(Media Select VCR)"); break;
case 0x93: DBGPrintf("(Media Select Tuner)"); break;
case 0x94: DBGPrintf("(Quit)"); break;
case 0x95: DBGPrintf("(Help)"); break;
case 0x96: DBGPrintf("(Media Select Tape)"); break;
case 0x97: DBGPrintf("(Media Select Cable)"); break;
case 0x98: DBGPrintf("(Media Select Satellite)"); break;
case 0x99: DBGPrintf("(Media Select Security)"); break;
case 0x9A: DBGPrintf("(Media Select Home)"); break;
case 0x9B: DBGPrintf("(Media Select Call)"); break;
case 0x9C: DBGPrintf("(Channel Increment)"); break;
case 0x9D: DBGPrintf("(Channel Decrement)"); break;
case 0x9E: DBGPrintf("(Media Select SAP)"); break;
case 0xA0: DBGPrintf("(VCR Plus)"); break;
case 0xA1: DBGPrintf("(Once)"); break;
case 0xA2: DBGPrintf("(Daily)"); break;
case 0xA3: DBGPrintf("(Weekly)"); break;
case 0xA4: DBGPrintf("(Monthly)"); break;
case 0xB0: DBGPrintf("(Play)"); break;
case 0xB1: DBGPrintf("(Pause)"); break;
case 0xB2: DBGPrintf("(Record)"); break;
case 0xB3: DBGPrintf("(Fast Forward)"); break;
case 0xB4: DBGPrintf("(Rewind)"); break;
case 0xB5: DBGPrintf("(Scan Next Track)"); break;
case 0xB6: DBGPrintf("(Scan Previous Track)"); break;
case 0xB7: DBGPrintf("(Stop)"); break;
case 0xB8: DBGPrintf("(Eject)"); break;
case 0xB9: DBGPrintf("(Random Play)"); break;
case 0xBA: DBGPrintf("(Select DisC)"); break;
case 0xBB: DBGPrintf("(Enter Disc)"); break;
case 0xBC: DBGPrintf("(Repeat)"); break;
case 0xBD: DBGPrintf("(Tracking)"); break;
case 0xBE: DBGPrintf("(Track Normal)"); break;
case 0xBF: DBGPrintf("(Slow Tracking)"); break;
case 0xC0: DBGPrintf("(Frame Forward)"); break;
case 0xC1: DBGPrintf("(Frame Back)"); break;
case 0xC2: DBGPrintf("(Mark)"); break;
case 0xC3: DBGPrintf("(Clear Mark)"); break;
case 0xC4: DBGPrintf("(Repeat From Mark)"); break;
case 0xC5: DBGPrintf("(Return To Mark)"); break;
case 0xC6: DBGPrintf("(Search Mark Forward)"); break;
case 0xC7: DBGPrintf("(Search Mark Backwards)"); break;
case 0xC8: DBGPrintf("(Counter Reset)"); break;
case 0xC9: DBGPrintf("(Show Counter)"); break;
case 0xCA: DBGPrintf("(Tracking Increment)"); break;
case 0xCB: DBGPrintf("(Tracking Decrement)"); break;
case 0xCD: DBGPrintf("(Pause/Continue)"); break;
case 0xE0: DBGPrintf("(Volume)"); break;
case 0xE1: DBGPrintf("(Balance)"); break;
case 0xE2: DBGPrintf("(Mute)"); break;
case 0xE3: DBGPrintf("(Bass)"); break;
case 0xE4: DBGPrintf("(Treble)"); break;
case 0xE5: DBGPrintf("(Bass Boost)"); break;
case 0xE6: DBGPrintf("(Surround Mode)"); break;
case 0xE7: DBGPrintf("(Loudness)"); break;
case 0xE8: DBGPrintf("(MPX)"); break;
case 0xE9: DBGPrintf("(Volume Up)"); break;
case 0xEA: DBGPrintf("(Volume Down)"); break;
case 0xF0: DBGPrintf("(Speed Select)"); break;
case 0xF1: DBGPrintf("(Playback Speed)"); break;
case 0xF2: DBGPrintf("(Standard Play)"); break;
case 0xF3: DBGPrintf("(Long Play)"); break;
case 0xF4: DBGPrintf("(Extended Play)"); break;
case 0xF5: DBGPrintf("(Slow)"); break;
case 0x100: DBGPrintf("(Fan Enable)"); break;
case 0x101: DBGPrintf("(Fan Speed)"); break;
case 0x102: DBGPrintf("(Light)"); break;
case 0x103: DBGPrintf("(Light Illumination Level)"); break;
case 0x104: DBGPrintf("(Climate Control Enable)"); break;
case 0x105: DBGPrintf("(Room Temperature)"); break;
case 0x106: DBGPrintf("(Security Enable)"); break;
case 0x107: DBGPrintf("(Fire Alarm)"); break;
case 0x108: DBGPrintf("(Police Alarm)"); break;
case 0x150: DBGPrintf("(Balance Right)"); break;
case 0x151: DBGPrintf("(Balance Left)"); break;
case 0x152: DBGPrintf("(Bass Increment)"); break;
case 0x153: DBGPrintf("(Bass Decrement)"); break;
case 0x154: DBGPrintf("(Treble Increment)"); break;
case 0x155: DBGPrintf("(Treble Decrement)"); break;
case 0x160: DBGPrintf("(Speaker System)"); break;
case 0x161: DBGPrintf("(Channel Left)"); break;
case 0x162: DBGPrintf("(Channel Right)"); break;
case 0x163: DBGPrintf("(Channel Center)"); break;
case 0x164: DBGPrintf("(Channel Front)"); break;
case 0x165: DBGPrintf("(Channel Center Front)"); break;
case 0x166: DBGPrintf("(Channel Side)"); break;
case 0x167: DBGPrintf("(Channel Surround)"); break;
case 0x168: DBGPrintf("(Channel Low Frequency Enhancement)"); break;
case 0x169: DBGPrintf("(Channel Top)"); break;
case 0x16A: DBGPrintf("(Channel Unknown)"); break;
case 0x170: DBGPrintf("(Sub-channel)"); break;
case 0x171: DBGPrintf("(Sub-channel Increment)"); break;
case 0x172: DBGPrintf("(Sub-channel Decrement)"); break;
case 0x173: DBGPrintf("(Alternate Audio Increment)"); break;
case 0x174: DBGPrintf("(Alternate Audio Decrement)"); break;
case 0x180: DBGPrintf("(Application Launch Buttons)"); break;
case 0x181: DBGPrintf("(AL Launch Button Configuration Tool)"); break;
case 0x182: DBGPrintf("(AL Programmable Button Configuration)"); break;
case 0x183: DBGPrintf("(AL Consumer Control Configuration)"); break;
case 0x184: DBGPrintf("(AL Word Processor)"); break;
case 0x185: DBGPrintf("(AL Text Editor)"); break;
case 0x186: DBGPrintf("(AL Spreadsheet)"); break;
case 0x187: DBGPrintf("(AL Graphics Editor)"); break;
case 0x188: DBGPrintf("(AL Presentation App)"); break;
case 0x189: DBGPrintf("(AL Database App)"); break;
case 0x18A: DBGPrintf("(AL Email Reader)"); break;
case 0x18B: DBGPrintf("(AL Newsreader)"); break;
case 0x18C: DBGPrintf("(AL Voicemail)"); break;
case 0x18D: DBGPrintf("(AL Contacts/Address Book)"); break;
case 0x18E: DBGPrintf("(AL Calendar/Schedule)"); break;
case 0x18F: DBGPrintf("(AL Task/Project Manager)"); break;
case 0x190: DBGPrintf("(AL Log/Journal/Timecard)"); break;
case 0x191: DBGPrintf("(AL Checkbook/Finance)"); break;
case 0x192: DBGPrintf("(AL Calculator)"); break;
case 0x193: DBGPrintf("(AL A/V Capture/Playback)"); break;
case 0x194: DBGPrintf("(AL Local Machine Browser)"); break;
case 0x195: DBGPrintf("(AL LAN/WAN Browser)"); break;
case 0x196: DBGPrintf("(AL Internet Browser)"); break;
case 0x197: DBGPrintf("(AL Remote Networking/ISP Connect)"); break;
case 0x198: DBGPrintf("(AL Network Conference)"); break;
case 0x199: DBGPrintf("(AL Network Chat)"); break;
case 0x19A: DBGPrintf("(AL Telephony/Dialer)"); break;
case 0x19B: DBGPrintf("(AL Logon)"); break;
case 0x19C: DBGPrintf("(AL Logoff)"); break;
case 0x19D: DBGPrintf("(AL Logon/Logoff)"); break;
case 0x19E: DBGPrintf("(AL Terminal Lock/Screensaver)"); break;
case 0x19F: DBGPrintf("(AL Control Panel)"); break;
case 0x1A0: DBGPrintf("(AL Command Line Processor/Run)"); break;
case 0x1A1: DBGPrintf("(AL Process/Task Manager)"); break;
case 0x1A2: DBGPrintf("(AL Select Tast/Application)"); break;
case 0x1A3: DBGPrintf("(AL Next Task/Application)"); break;
case 0x1A4: DBGPrintf("(AL Previous Task/Application)"); break;
case 0x1A5: DBGPrintf("(AL Preemptive Halt Task/Application)"); break;
case 0x200: DBGPrintf("(Generic GUI Application Controls)"); break;
case 0x201: DBGPrintf("(AC New)"); break;
case 0x202: DBGPrintf("(AC Open)"); break;
case 0x203: DBGPrintf("(AC Close)"); break;
case 0x204: DBGPrintf("(AC Exit)"); break;
case 0x205: DBGPrintf("(AC Maximize)"); break;
case 0x206: DBGPrintf("(AC Minimize)"); break;
case 0x207: DBGPrintf("(AC Save)"); break;
case 0x208: DBGPrintf("(AC Print)"); break;
case 0x209: DBGPrintf("(AC Properties)"); break;
case 0x21A: DBGPrintf("(AC Undo)"); break;
case 0x21B: DBGPrintf("(AC Copy)"); break;
case 0x21C: DBGPrintf("(AC Cut)"); break;
case 0x21D: DBGPrintf("(AC Paste)"); break;
case 0x21E: DBGPrintf("(AC Select All)"); break;
case 0x21F: DBGPrintf("(AC Find)"); break;
case 0x220: DBGPrintf("(AC Find and Replace)"); break;
case 0x221: DBGPrintf("(AC Search)"); break;
case 0x222: DBGPrintf("(AC Go To)"); break;
case 0x223: DBGPrintf("(AC Home)"); break;
case 0x224: DBGPrintf("(AC Back)"); break;
case 0x225: DBGPrintf("(AC Forward)"); break;
case 0x226: DBGPrintf("(AC Stop)"); break;
case 0x227: DBGPrintf("(AC Refresh)"); break;
case 0x228: DBGPrintf("(AC Previous Link)"); break;
case 0x229: DBGPrintf("(AC Next Link)"); break;
case 0x22A: DBGPrintf("(AC Bookmarks)"); break;
case 0x22B: DBGPrintf("(AC History)"); break;
case 0x22C: DBGPrintf("(AC Subscriptions)"); break;
case 0x22D: DBGPrintf("(AC Zoom In)"); break;
case 0x22E: DBGPrintf("(AC Zoom Out)"); break;
case 0x22F: DBGPrintf("(AC Zoom)"); break;
case 0x230: DBGPrintf("(AC Full Screen View)"); break;
case 0x231: DBGPrintf("(AC Normal View)"); break;
case 0x232: DBGPrintf("(AC View Toggle)"); break;
case 0x233: DBGPrintf("(AC Scroll Up)"); break;
case 0x234: DBGPrintf("(AC Scroll Down)"); break;
case 0x235: DBGPrintf("(AC Scroll)"); break;
case 0x236: DBGPrintf("(AC Pan Left)"); break;
case 0x237: DBGPrintf("(AC Pan Right)"); break;
case 0x238: DBGPrintf("(AC Pan)"); break;
case 0x239: DBGPrintf("(AC New Window)"); break;
case 0x23A: DBGPrintf("(AC Tile Horizontally)"); break;
case 0x23B: DBGPrintf("(AC Tile Vertically)"); break;
case 0x23C: DBGPrintf("(AC Format)"); break;
default: DBGPrintf("(?)"); break;
}
break;
}
}
#endif
//=============================================================================
// Lets try copy of the HID Parse code and see what happens with with it.
//=============================================================================
// Extract 1 to 32 bits from the data array, starting at bitindex.
static uint32_t bitfield(const uint8_t *data, uint32_t bitindex, uint32_t numbits)
{
uint32_t output = 0;
uint32_t bitcount = 0;
data += (bitindex >> 3);
uint32_t offset = bitindex & 7;
if (offset) {
output = (*data++) >> offset;
bitcount = 8 - offset;
}
while (bitcount < numbits) {
output |= (uint32_t)(*data++) << bitcount;
bitcount += 8;
}
if (bitcount > numbits && numbits < 32) {
output &= ((1 << numbits) - 1);
}
return output;
}
// convert a number with the specified number of bits from unsigned to signed,
// so the result is a proper 32 bit signed integer.
static int32_t signext(uint32_t num, uint32_t bitcount)
{
if (bitcount < 32 && bitcount > 0 && (num & (1 << (bitcount - 1)))) {
num |= ~((1 << bitcount) - 1);
}
return (int32_t)num;
}
// convert a tag's value to a signed integer.
static int32_t signedval(uint32_t num, uint8_t tag)
{
tag &= 3;
if (tag == 1) return (int8_t)num;
if (tag == 2) return (int16_t)num;
return (int32_t)num;
}
// This no-inputs parse is meant to be used when we first get the
// HID report descriptor. It finds all the top level collections
// and allows drivers to claim them. This is always where we
// learn whether the reports will or will not use a Report ID byte.
void BluetoothConnection::parse()
{
DBGPrintf("BluetoothConnection::parse() called\n");
const uint8_t *p = descriptor_;
const uint8_t *end = p + descsize_;
uint16_t usage_page = 0;
uint16_t usage = 0;
uint8_t collection_level = 0;
uint8_t topusage_count = 0;
//bool use_report_id = false;
while (p < end) {
uint8_t tag = *p;
if (tag == 0xFE) { // Long Item
p += *p + 3;
continue;
}
uint32_t val = 0;
switch (tag & 0x03) { // Short Item data
case 0: val = 0;
p++;
break;
case 1: val = p[1];
p += 2;
break;
case 2: val = p[1] | (p[2] << 8);
p += 3;
break;
case 3: val = p[1] | (p[2] << 8) | (p[3] << 16) | (p[4] << 24);
p += 5;
break;
}
if (p > end) break;
switch (tag & 0xFC) {
case 0x84: // Report ID (global)
//use_report_id = true;
break;
case 0x04: // Usage Page (global)
usage_page = val;
break;
case 0x08: // Usage (local)
usage = val;
break;
case 0xA0: // Collection
if (collection_level == 0 && topusage_count < TOPUSAGE_LIST_LEN) {
uint32_t topusage = ((uint32_t)usage_page << 16) | usage;
println("Found top level collection ", topusage, HEX);
DBGPrintf("\ttopusage:%x\n", topusage);
//topusage_list[topusage_count] = topusage;
topusage_drivers[topusage_count] = find_driver(topusage);
topusage_count++;
}
collection_level++;
usage = 0;
break;
case 0xC0: // End Collection
if (collection_level > 0) {
collection_level--;
}
case 0x80: // Input
case 0x90: // Output
case 0xB0: // Feature
usage = 0;
break;
}
}
while (topusage_count < TOPUSAGE_LIST_LEN) {
//topusage_list[topusage_count] = 0;
topusage_drivers[topusage_count] = NULL;
topusage_count++;
}
}
BTHIDInput * BluetoothConnection::find_driver(uint32_t topusage)
{
println("find_driver");
BTHIDInput *driver = BluetoothController::available_bthid_drivers_list;
hidclaim_t claim_type;
while (driver) {
//println(" driver ", (uint32_t)driver, HEX);
if ((claim_type = driver->bt_claim_collection(this, device_class_, topusage)) != CLAIM_NO) {
//if (claim_type == CLAIM_INTERFACE) hid_driver_claimed_control_ = true;
return driver;
}
driver = driver->next;
}
println("No Driver claimed topusage: ", topusage, HEX);
return NULL;
}
void BluetoothConnection::parse(uint16_t type_and_report_id, const uint8_t *data, uint32_t len)
{
const bool use_report_id = true;
const uint8_t *p = descriptor_;
const uint8_t *end = p + descsize_;
BTHIDInput *driver = NULL;
// USBHIDInput *driver = hidi_; // hack for now everything feeds back to us...
uint32_t topusage = 0;
uint8_t topusage_index = 0;
uint8_t collection_level = 0;
uint16_t usage[USAGE_LIST_LEN] = {0, 0};
uint8_t usage_count = 0;
uint8_t usage_min_max_count = 0;
uint8_t usage_min_max_mask = 0;
uint8_t report_id = 0;
uint16_t report_size = 0;
uint16_t report_count = 0;
uint16_t usage_page = 0;
uint32_t last_usage = 0;
int32_t logical_min = 0;
int32_t logical_max = 0;
uint32_t bitindex = 0;
while (p < end) {
uint8_t tag = *p;
if (tag == 0xFE) { // Long Item (unsupported)
p += p[1] + 3;
continue;
}
uint32_t val = 0;
switch (tag & 0x03) { // Short Item data
case 0: val = 0;
p++;
break;
case 1: val = p[1];
p += 2;
break;
case 2: val = p[1] | (p[2] << 8);
p += 3;
break;
case 3: val = p[1] | (p[2] << 8) | (p[3] << 16) | (p[4] << 24);
p += 5;
break;
}
if (p > end) break;
bool reset_local = false;
switch (tag & 0xFC) {
case 0x04: // Usage Page (global)
usage_page = val;
break;
case 0x14: // Logical Minimum (global)
logical_min = signedval(val, tag);
break;
case 0x24: // Logical Maximum (global)
logical_max = signedval(val, tag);
break;
case 0x74: // Report Size (global)
report_size = val;
break;
case 0x94: // Report Count (global)
report_count = val;
break;
case 0x84: // Report ID (global)
report_id = val;
break;
case 0x08: // Usage (local)
if (usage_count < USAGE_LIST_LEN) {
// Usages: 0 is reserved 0x1-0x1f is sort of reserved for top level things like
// 0x1 - Pointer - A collection... So lets try ignoring these
if (val > 0x1f) {
usage[usage_count++] = val;
}
}
break;
case 0x18: // Usage Minimum (local)
// Note: Found a report with multiple min/max
if (usage_count != 255) {
usage_count = 255;
usage_min_max_count = 0;
usage_min_max_mask = 0;
}
usage[usage_min_max_count * 2] = val;
usage_min_max_mask |= 1;
if (usage_min_max_mask == 3) {
usage_min_max_count++;
usage_min_max_mask = 0;
}
break;
case 0x28: // Usage Maximum (local)
if (usage_count != 255) {
usage_count = 255;
usage_min_max_count = 0;
usage_min_max_mask = 0;
}
usage[usage_min_max_count * 2 + 1] = val;
usage_min_max_mask |= 2;
if (usage_min_max_mask == 3) {
usage_min_max_count++;
usage_min_max_mask = 0;
}
break;
case 0xA0: // Collection
if (collection_level == 0) {
topusage = ((uint32_t)usage_page << 16) | usage[0];
driver = NULL;
if (topusage_index < TOPUSAGE_LIST_LEN) {
driver = topusage_drivers[topusage_index++];
}
}
// discard collection info if not top level, hopefully that's ok?
collection_level++;
reset_local = true;
break;
case 0xC0: // End Collection
if (collection_level > 0) {
collection_level--;
if (collection_level == 0 && driver != NULL) {
driver->bt_hid_input_end();
//driver = NULL;
}
}
reset_local = true;
break;
case 0x80: // Input
if (use_report_id && (report_id != (type_and_report_id & 0xFF))) {
// completely ignore and do not advance bitindex
// for descriptors of other report IDs
reset_local = true;
break;
}
if ((val & 1) || (driver == NULL)) {
// skip past constant fields or when no driver is listening
bitindex += report_count * report_size;
} else {
println("begin, usage=", topusage, HEX);
println(" type= ", val, HEX);
println(" min= ", logical_min);
println(" max= ", logical_max);
println(" reportcount=", report_count);
println(" usage count=", usage_count);
println(" usage min max count=", usage_min_max_count);
driver->bt_hid_input_begin(topusage, val, logical_min, logical_max);
println("Input, total bits=", report_count * report_size);
if ((val & 2)) {
// ordinary variable format
uint32_t uindex = 0;
uint32_t uindex_max = 0xffff; // assume no MAX
bool uminmax = false;
uint8_t uminmax_index = 0;
if (usage_count > USAGE_LIST_LEN) {
// usage numbers by min/max, not from list
uindex = usage[0];
uindex_max = usage[1];
uminmax = true;
} else if ((report_count > 1) && (usage_count <= 1)) {
// Special cases: Either only one or no usages specified and there are more than one
// report counts .
if (usage_count == 1) {
uindex = usage[0];
} else {
// BUGBUG:: Not sure good place to start? maybe round up from last usage to next higher group up of 0x100?
uindex = (last_usage & 0xff00) + 0x100;
}
uminmax = true;
}
//USBHDBGDBGSerial.printf("TU:%x US:%x %x %d %d: C:%d, %d, MM:%d, %x %x\n", topusage, usage_page, val, logical_min, logical_max,
// report_count, usage_count, uminmax, usage[0], usage[1]);
for (uint32_t i = 0; i < report_count; i++) {
uint32_t u;
if (uminmax) {
u = uindex;
if (uindex < uindex_max) uindex++;
else if (uminmax_index < usage_min_max_count) {
uminmax_index++;
uindex = usage[uminmax_index * 2];
uindex_max = usage[uminmax_index * 2 + 1];
//USBHDBGPDBGSerial.printf("$$ next min/max pair: %u %u %u\n", uminmax_index, uindex, uindex_max);
}
} else {
u = usage[uindex++];
if (uindex >= USAGE_LIST_LEN - 1) {
uindex = USAGE_LIST_LEN - 1;
}
}
last_usage = u; // remember the last one we used...
u |= (uint32_t)usage_page << 16;
print(" usage = ", u, HEX);
uint32_t n = bitfield(data, bitindex, report_size);
if (logical_min >= 0) {
println(" data = ", n);
driver->bt_hid_input_data(u, n);
} else {
int32_t sn = signext(n, report_size);
println(" sdata = ", sn);
driver->bt_hid_input_data(u, sn);
}
bitindex += report_size;
}
} else {
// array format, each item is a usage number
// maybe act like the 2 case...
if (usage_min_max_count && (report_size == 1)) {
uint32_t uindex = usage[0];
uint32_t uindex_max = usage[1];
uint8_t uminmax_index = 0;
uint32_t u;
for (uint32_t i = 0; i < report_count; i++) {
u = uindex;
if (uindex < uindex_max) uindex++;
else if (uminmax_index < usage_min_max_count) {
uminmax_index++;
uindex = usage[uminmax_index * 2];
uindex_max = usage[uminmax_index * 2 + 1];
//USBHDBGPDBGSerial.printf("$$ next min/max pair: %u %u %u\n", uminmax_index, uindex, uindex_max);
}
u |= (uint32_t)usage_page << 16;
uint32_t n = bitfield(data, bitindex, report_size);
if (logical_min >= 0) {
println(" data = ", n);
driver->bt_hid_input_data(u, n);
} else {
int32_t sn = signext(n, report_size);
println(" sdata = ", sn);
driver->bt_hid_input_data(u, sn);
}
bitindex += report_size;
}
} else {
for (uint32_t i = 0; i < report_count; i++) {
uint32_t u = bitfield(data, bitindex, report_size);
int n = u;
if (n >= logical_min && n <= logical_max) {
u |= (uint32_t)usage_page << 16;
print(" usage = ", u, HEX);
println(" data = 1");
driver->bt_hid_input_data(u, 1);
} else {
print (" usage =", u, HEX);
print(" out of range: ", logical_min, HEX);
println(" ", logical_max, HEX);
}
bitindex += report_size;
}
}
}
}
reset_local = true;
break;
case 0x90: // Output
// TODO.....
reset_local = true;
break;
case 0xB0: // Feature
// TODO.....
reset_local = true;
break;
case 0x34: // Physical Minimum (global)
case 0x44: // Physical Maximum (global)
case 0x54: // Unit Exponent (global)
case 0x64: // Unit (global)
break; // Ignore these commonly used tags. Hopefully not needed?
case 0xA4: // Push (yikes! Hope nobody really uses this?!)
case 0xB4: // Pop (yikes! Hope nobody really uses this?!)
case 0x38: // Designator Index (local)
case 0x48: // Designator Minimum (local)
case 0x58: // Designator Maximum (local)
case 0x78: // String Index (local)
case 0x88: // String Minimum (local)
case 0x98: // String Maximum (local)
case 0xA8: // Delimiter (local)
default:
println("Ruh Roh, unsupported tag, not a good thing Scoob ", tag, HEX);
break;
}
if (reset_local) {
usage_count = 0;
usage_min_max_count = 0;
usage[0] = 0;
usage[1] = 0;
}
}
}