Again still WIP - but now have the Bluetooth object talking through a keyboard object...main
// your best effort to read chapter 4 before asking USB questions! | // your best effort to read chapter 4 before asking USB questions! | ||||
//#define USBHOST_PRINT_DEBUG | |||||
#define USBHOST_PRINT_DEBUG | |||||
/************************************************/ | /************************************************/ | ||||
/* Data Types */ | /* Data Types */ | ||||
Device_t *mydevice = NULL; | Device_t *mydevice = NULL; | ||||
}; | }; | ||||
// Device drivers may inherit from this base class, if they wish to receive | |||||
// HID input like data from Bluetooth HID device. | |||||
class BluetoothController; | |||||
class BTHIDInput { | |||||
public: | |||||
operator bool() { return (btdevice != nullptr); } | |||||
uint16_t idVendor() { return (btdevice != nullptr) ? btdevice->idVendor : 0; } | |||||
uint16_t idProduct() { return (btdevice != nullptr) ? btdevice->idProduct : 0; } | |||||
const uint8_t *manufacturer() | |||||
{ return ((btdevice == nullptr) || (btdevice->strbuf == nullptr)) ? nullptr : &btdevice->strbuf->buffer[btdevice->strbuf->iStrings[strbuf_t::STR_ID_MAN]]; } | |||||
const uint8_t *product() | |||||
{ return ((btdevice == nullptr) || (btdevice->strbuf == nullptr)) ? nullptr : &btdevice->strbuf->buffer[btdevice->strbuf->iStrings[strbuf_t::STR_ID_PROD]]; } | |||||
const uint8_t *serialNumber() | |||||
{ return ((btdevice == nullptr) || (btdevice->strbuf == nullptr)) ? nullptr : &btdevice->strbuf->buffer[btdevice->strbuf->iStrings[strbuf_t::STR_ID_SERIAL]]; } | |||||
private: | |||||
virtual bool claim_bluetooth(BluetoothController *driver, uint32_t bluetooth_class) {return false;} | |||||
virtual bool process_bluetooth_HID_data(const uint8_t *data, uint16_t length) {return false;} | |||||
virtual void release_bluetooth() {}; | |||||
void add_to_list(); | |||||
BTHIDInput *next; | |||||
friend class BluetoothController; | |||||
protected: | |||||
Device_t *btdevice = NULL; | |||||
}; | |||||
/************************************************/ | /************************************************/ | ||||
/* USB Device Drivers */ | /* USB Device Drivers */ | ||||
/************************************************/ | /************************************************/ | ||||
//-------------------------------------------------------------------------- | //-------------------------------------------------------------------------- | ||||
class KeyboardController : public USBDriver , public USBHIDInput { | |||||
class KeyboardController : public USBDriver , public USBHIDInput, public BTHIDInput { | |||||
public: | public: | ||||
typedef union { | typedef union { | ||||
struct { | struct { | ||||
KeyboardController(USBHost *host) { init(); } | KeyboardController(USBHost *host) { init(); } | ||||
// Some methods are in both public classes so we need to figure out which one to use | // Some methods are in both public classes so we need to figure out which one to use | ||||
operator bool() { return (device != nullptr); } | |||||
uint16_t idVendor(); | |||||
uint16_t idProduct(); | |||||
const uint8_t *manufacturer(); | |||||
const uint8_t *product(); | |||||
const uint8_t *serialNumber(); | |||||
operator bool() { return ((device != nullptr) || (btdevice != nullptr)); } | |||||
// Main boot keyboard functions. | // Main boot keyboard functions. | ||||
uint16_t getKey() { return keyCode; } | uint16_t getKey() { return keyCode; } | ||||
uint8_t getModifiers() { return modifiers; } | uint8_t getModifiers() { return modifiers; } | ||||
void new_data(const Transfer_t *transfer); | void new_data(const Transfer_t *transfer); | ||||
void init(); | void init(); | ||||
// Bluetooth data | |||||
bool claim_bluetooth(BluetoothController *driver, uint32_t bluetooth_class); | |||||
bool process_bluetooth_HID_data(const uint8_t *data, uint16_t length); | |||||
void release_bluetooth(); | |||||
protected: // HID functions for extra keyboard data. | protected: // HID functions for extra keyboard data. | ||||
virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage); | virtual hidclaim_t claim_collection(USBHIDParser *driver, Device_t *dev, uint32_t topusage); | ||||
virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax); | virtual void hid_input_begin(uint32_t topusage, uint32_t type, int lgmin, int lgmax); | ||||
enum {MAX_ENDPOINTS=4, NUM_SERVICES=4, }; // Max number of Bluetooth services - if you need more than 4 simply increase this number | enum {MAX_ENDPOINTS=4, NUM_SERVICES=4, }; // Max number of Bluetooth services - if you need more than 4 simply increase this number | ||||
enum {BT_CLASS_DEVICE= 0x0804}; // Toy - Robot | enum {BT_CLASS_DEVICE= 0x0804}; // Toy - Robot | ||||
static void driver_ready_for_bluetooth(BTHIDInput *driver); | |||||
protected: | protected: | ||||
virtual bool claim(Device_t *device, int type, const uint8_t *descriptors, uint32_t len); | virtual bool claim(Device_t *device, int type, const uint8_t *descriptors, uint32_t len); | ||||
virtual void control(const Transfer_t *transfer); | virtual void control(const Transfer_t *transfer); | ||||
virtual void disconnect(); | virtual void disconnect(); | ||||
//virtual void timer_event(USBDriverTimer *whichTimer); | //virtual void timer_event(USBDriverTimer *whichTimer); | ||||
BTHIDInput * find_driver(uint32_t device_type); | |||||
private: | private: | ||||
static void rx_callback(const Transfer_t *transfer); | static void rx_callback(const Transfer_t *transfer); | ||||
static void rx2_callback(const Transfer_t *transfer); | static void rx2_callback(const Transfer_t *transfer); | ||||
void inline sendHCIRemoteNameRequest(); | void inline sendHCIRemoteNameRequest(); | ||||
//void inline sendHCIReadBufferSize(); | |||||
//void inline sendHCIReadClassOfDevice(); | |||||
//void inline sendHCIReadLocalSupportedCommands(); | |||||
//void inline sendHCIReadLocalName(); | |||||
//void inline sendHCIReadVoiceSettings(); | |||||
//void inline sendHCICommandReadNumberSupportedIAC(); | |||||
//void inline sendHCICommandReadCurrentIACLAP(); | |||||
//void inline sendHCIClearAllEventFilters(); | |||||
//void inline sendHCIWriteConnectionAcceptTimeout(); | |||||
//void inline sendHCILEReadBufferSize(); | |||||
//void inline sendHCILEReadLocalSupportedFeatures(); | |||||
//void inline sendHCILEReadSupportedStates(); | |||||
//void inline sendHCIWriteInquiryMode(); | |||||
//void inline sendHCIReadInquiryResponseTransmitPowerLevel(); | |||||
//void inline sendHCIReadLocalExtendedFeatures(uint8_t page); | |||||
//void inline sendHCISetEventMask(); | |||||
//void inline sendHCIReadStoredLinkKey(); | |||||
//void inline sendHCIWriteDefaultLinkPolicySettings(); | |||||
//void inline sendHCIReadPageScanActivity(); | |||||
//void inline sendHCIReadPageScanType(); | |||||
//void inline sendHCILESetEventMask(); | |||||
//void inline sendHCILEReadADVTXPower(); | |||||
//void inline sendHCIEReadWhiteListSize(); | |||||
//void inline sendHCILEClearWhiteList(); | |||||
//void inline sendHCIDeleteStoredLinkKey(); | |||||
//void inline sendHCIWriteLocalName(); | |||||
//void inline sendHCIWriteSSPMode(uint8_t ssp_mode); | |||||
//void inline sendHCIWriteEIR(); | |||||
//void inline sendHCIWriteLEHostSupported(); | |||||
//void inline sendHCILESetAdvData () ; | |||||
//void inline sendHCILESetScanRSPData(); | |||||
void handle_hci_command_complete(); | void handle_hci_command_complete(); | ||||
void handle_hci_command_status(); | void handle_hci_command_status(); | ||||
void handle_hci_inquiry_result(); | void handle_hci_inquiry_result(); | ||||
void setHIDProtocol(uint8_t protocol); | void setHIDProtocol(uint8_t protocol); | ||||
void handleHIDTHDRData(uint8_t *buffer); // Pass the whole buffer... | void handleHIDTHDRData(uint8_t *buffer); // Pass the whole buffer... | ||||
static BTHIDInput *available_bthid_drivers_list; | |||||
setup_t setup; | setup_t setup; | ||||
const char *pair_pincode_; // What pin code to use for the pairing | const char *pair_pincode_; // What pin code to use for the pairing | ||||
uint8_t my_bdaddr[6]; // The bluetooth dongles Bluetooth address. | uint8_t my_bdaddr[6]; // The bluetooth dongles Bluetooth address. | ||||
uint8_t features[8]; // remember our local features. | uint8_t features[8]; // remember our local features. | ||||
BTHIDInput * device_driver_ = nullptr;; | |||||
uint8_t device_bdaddr_[6];// remember devices address | uint8_t device_bdaddr_[6];// remember devices address | ||||
uint8_t device_ps_repetion_mode_ ; // mode | uint8_t device_ps_repetion_mode_ ; // mode | ||||
uint8_t device_clock_offset_[2]; | uint8_t device_clock_offset_[2]; |
// Setup some states for the TX pipe where we need to chain messages | // Setup some states for the TX pipe where we need to chain messages | ||||
enum {STATE_TX_SEND_CONNECT_INT=200, STATE_TX_SEND_CONECT_RSP_SUCCESS, STATE_TX_SEND_CONFIG_REQ}; | enum {STATE_TX_SEND_CONNECT_INT=200, STATE_TX_SEND_CONECT_RSP_SUCCESS, STATE_TX_SEND_CONFIG_REQ}; | ||||
// 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. | |||||
BTHIDInput * BluetoothController::available_bthid_drivers_list = NULL; | |||||
void BluetoothController::driver_ready_for_bluetooth(BTHIDInput *driver) | |||||
{ | |||||
driver->next = NULL; | |||||
if (available_bthid_drivers_list == NULL) { | |||||
available_bthid_drivers_list = driver; | |||||
} else { | |||||
BTHIDInput *last = available_bthid_drivers_list; | |||||
while (last->next) last = last->next; | |||||
last->next = driver; | |||||
} | |||||
} | |||||
// 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. | |||||
BTHIDInput * BluetoothController::find_driver(uint32_t device_type) | |||||
{ | |||||
Serial.printf("BluetoothController::find_driver"); | |||||
BTHIDInput *driver = available_bthid_drivers_list; | |||||
while (driver) { | |||||
Serial.printf(" driver %x\n", (uint32_t)driver); | |||||
if (driver->claim_bluetooth(this, device_type)) { | |||||
Serial.printf(" *** Claimed ***\n"); | |||||
return driver; | |||||
} | |||||
driver = driver->next; | |||||
} | |||||
return NULL; | |||||
} | |||||
/************************************************************/ | /************************************************************/ | ||||
// Initialization and claiming of devices & interfaces | // Initialization and claiming of devices & interfaces | ||||
/************************************************************/ | /************************************************************/ | ||||
void BluetoothController::disconnect() | void BluetoothController::disconnect() | ||||
{ | { | ||||
Serial.printf("Bluetooth Disconnect"); | |||||
if (device_driver_) { | |||||
device_driver_->release_bluetooth(); | |||||
device_driver_ = nullptr; | |||||
} | |||||
} | } | ||||
// BUGBUG, lets hard code to go to new state... | // BUGBUG, lets hard code to go to new state... | ||||
for (uint8_t i = 0; i < 6; i++) device_bdaddr_[i] = rxbuf_[index_bd+i]; | for (uint8_t i = 0; i < 6; i++) device_bdaddr_[i] = rxbuf_[index_bd+i]; | ||||
device_class_ = bluetooth_class; | device_class_ = bluetooth_class; | ||||
device_driver_ = find_driver(device_class_); | |||||
device_ps_repetion_mode_ = rxbuf_[index_ps]; // mode | device_ps_repetion_mode_ = rxbuf_[index_ps]; // mode | ||||
device_clock_offset_[0] = rxbuf_[index_clock_offset]; | device_clock_offset_[0] = rxbuf_[index_clock_offset]; | ||||
device_clock_offset_[1] = rxbuf_[index_clock_offset+1]; | device_clock_offset_[1] = rxbuf_[index_clock_offset+1]; | ||||
DBGPrintf(" Peripheral device\n"); | DBGPrintf(" Peripheral device\n"); | ||||
if (class_of_device & 0x80) DBGPrintf(" Mouse\n"); | if (class_of_device & 0x80) DBGPrintf(" Mouse\n"); | ||||
if (class_of_device & 0x40) DBGPrintf(" Keyboard\n"); | if (class_of_device & 0x40) DBGPrintf(" Keyboard\n"); | ||||
device_driver_ = find_driver(class_of_device); | |||||
// We need to save away the BDADDR and class link type? | // We need to save away the BDADDR and class link type? | ||||
for(uint8_t i=0; i<6; i++) device_bdaddr_[i] = rxbuf_[i+2]; | for(uint8_t i=0; i<6; i++) device_bdaddr_[i] = rxbuf_[i+2]; | ||||
//5 4 0 48 0 13 | //5 4 0 48 0 13 | ||||
DBGPrintf(" Event: HCI Disconnect complete(%d): handle: %x, reason:%x\n", rxbuf_[2], | DBGPrintf(" Event: HCI Disconnect complete(%d): handle: %x, reason:%x\n", rxbuf_[2], | ||||
rxbuf_[3]+(rxbuf_[4]<<8), rxbuf_[5]); | rxbuf_[3]+(rxbuf_[4]<<8), rxbuf_[5]); | ||||
if (device_driver_) { | |||||
device_driver_->release_bluetooth(); | |||||
device_driver_ = nullptr; | |||||
} | |||||
// Probably should clear out connection data. | |||||
device_class_ = 0; | |||||
memset(device_bdaddr_, 0, sizeof(device_bdaddr_)); | |||||
//... | |||||
} | } | ||||
void BluetoothController::handle_hci_authentication_complete() | void BluetoothController::handle_hci_authentication_complete() | ||||
data[8]+((uint16_t)data[9] << 8), data[10]+((uint16_t)data[11] << 8)); | data[8]+((uint16_t)data[9] << 8), data[10]+((uint16_t)data[11] << 8)); | ||||
if (scid == control_dcid_) { | if (scid == control_dcid_) { | ||||
// Set HID Boot mode | // Set HID Boot mode | ||||
setHIDProtocol(HID_BOOT_PROTOCOL); | |||||
//setHIDProtocol(HID_BOOT_PROTOCOL); // | |||||
setHIDProtocol(HID_RPT_PROTOCOL); //HID_RPT_PROTOCOL | |||||
if (do_pair_device_) { | if (do_pair_device_) { | ||||
// Tell system we will next need to setup connection for the interrupt | // Tell system we will next need to setup connection for the interrupt | ||||
pending_control_tx_ = STATE_TX_SEND_CONNECT_INT; | pending_control_tx_ = STATE_TX_SEND_CONNECT_INT; | ||||
DBGPrintf("HID HDR Data: len: %d, Type: %d\n", len, data[9]); | DBGPrintf("HID HDR Data: len: %d, Type: %d\n", len, data[9]); | ||||
// ??? How to parse??? Use HID object??? | // ??? How to parse??? Use HID object??? | ||||
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"); | |||||
if (device_driver_) { | |||||
device_driver_->process_bluetooth_HID_data(&data[9], len-1); // We skip the first byte... | |||||
} 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"); | |||||
} | |||||
} | } | ||||
} | } |
#define print USBHost::print_ | #define print USBHost::print_ | ||||
#define println USBHost::println_ | #define println USBHost::println_ | ||||
uint16_t KeyboardController::idVendor() | |||||
{ | |||||
if (device != nullptr) return device->idVendor; | |||||
if (btdevice != nullptr) return btdevice->idVendor; | |||||
return 0; | |||||
} | |||||
uint16_t KeyboardController::idProduct() | |||||
{ | |||||
if (device != nullptr) return device->idProduct; | |||||
if (btdevice != nullptr) return btdevice->idProduct; | |||||
return 0; | |||||
} | |||||
const uint8_t *KeyboardController::manufacturer() | |||||
{ | |||||
if ((device != nullptr) && (device->strbuf != nullptr)) return &device->strbuf->buffer[device->strbuf->iStrings[strbuf_t::STR_ID_MAN]]; | |||||
if ((btdevice != nullptr) && (btdevice->strbuf != nullptr)) return &btdevice->strbuf->buffer[btdevice->strbuf->iStrings[strbuf_t::STR_ID_MAN]]; | |||||
return nullptr; | |||||
} | |||||
const uint8_t *KeyboardController::product() | |||||
{ | |||||
if ((device != nullptr) && (device->strbuf != nullptr)) return &device->strbuf->buffer[device->strbuf->iStrings[strbuf_t::STR_ID_PROD]]; | |||||
if ((btdevice != nullptr) && (btdevice->strbuf != nullptr)) return &btdevice->strbuf->buffer[btdevice->strbuf->iStrings[strbuf_t::STR_ID_PROD]]; | |||||
return nullptr; | |||||
} | |||||
const uint8_t *KeyboardController::serialNumber() | |||||
{ | |||||
if ((device != nullptr) && (device->strbuf != nullptr)) return &device->strbuf->buffer[device->strbuf->iStrings[strbuf_t::STR_ID_SERIAL]]; | |||||
if ((btdevice != nullptr) && (btdevice->strbuf != nullptr)) return &btdevice->strbuf->buffer[btdevice->strbuf->iStrings[strbuf_t::STR_ID_SERIAL]]; | |||||
return nullptr; | |||||
} | |||||
void KeyboardController::init() | void KeyboardController::init() | ||||
{ | { | ||||
contribute_Pipes(mypipes, sizeof(mypipes)/sizeof(Pipe_t)); | contribute_Pipes(mypipes, sizeof(mypipes)/sizeof(Pipe_t)); | ||||
contribute_String_Buffers(mystring_bufs, sizeof(mystring_bufs)/sizeof(strbuf_t)); | contribute_String_Buffers(mystring_bufs, sizeof(mystring_bufs)/sizeof(strbuf_t)); | ||||
driver_ready_for_device(this); | driver_ready_for_device(this); | ||||
USBHIDParser::driver_ready_for_hid_collection(this); | USBHIDParser::driver_ready_for_hid_collection(this); | ||||
BluetoothController::driver_ready_for_bluetooth(this); | |||||
} | } | ||||
bool KeyboardController::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) | bool KeyboardController::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) | ||||
void KeyboardController::updateLEDS() { | void KeyboardController::updateLEDS() { | ||||
// Now lets tell keyboard new state. | // Now lets tell keyboard new state. | ||||
mk_setup(setup, 0x21, 9, 0x200, 0, sizeof(leds_.byte)); // hopefully this sets leds | |||||
queue_Control_Transfer(device, &setup, &leds_.byte, this); | |||||
if (device != nullptr) { | |||||
// Only do it this way if we are a standard USB device | |||||
mk_setup(setup, 0x21, 9, 0x200, 0, sizeof(leds_.byte)); // hopefully this sets leds | |||||
queue_Control_Transfer(device, &setup, &leds_.byte, this); | |||||
} else { | |||||
// Bluetooth, need to setup back channel to Bluetooth controller. | |||||
} | |||||
} | } | ||||
//============================================================================= | //============================================================================= | ||||
hid_input_begin_ = false; | hid_input_begin_ = false; | ||||
} | } | ||||
} | } | ||||
bool KeyboardController::claim_bluetooth(BluetoothController *driver, uint32_t bluetooth_class) | |||||
{ | |||||
Serial.printf("Keyboard Controller::claim_bluetooth - Class %x\n", bluetooth_class); | |||||
if ((((bluetooth_class & 0xff00) == 0x2500) || (((bluetooth_class & 0xff00) == 0x500))) && (bluetooth_class & 0x40)) { | |||||
Serial.printf("KeyboardController::claim_bluetooth TRUE\n"); | |||||
//btdevice = driver; | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
bool KeyboardController::process_bluetooth_HID_data(const uint8_t *data, uint16_t length) | |||||
{ | |||||
// Example DATA from bluetooth keyboard: | |||||
// 0 1 2 3 4 5 6 7 8 910 1 2 3 4 5 6 7 | |||||
// LEN D | |||||
//BT rx2_data(18): 48 20 e 0 a 0 70 0 a1 1 2 0 0 0 0 0 0 0 | |||||
//BT rx2_data(18): 48 20 e 0 a 0 70 0 a1 1 2 0 4 0 0 0 0 0 | |||||
//BT rx2_data(18): 48 20 e 0 a 0 70 0 a1 1 2 0 0 0 0 0 0 0 | |||||
// So Len=9 passed in data starting at report ID=1... | |||||
Serial.printf("KeyboardController::process_bluetooth_HID_data\n"); | |||||
if (data[0] != 1) return false; | |||||
print(" KB Data: "); | |||||
print_hexbytes(data, length); | |||||
for (int i=2; i < length; i++) { | |||||
uint32_t key = prev_report[i]; | |||||
if (key >= 4 && !contains(key, report)) { | |||||
key_release(prev_report[0], key); | |||||
} | |||||
} | |||||
for (int i=2; i < 8; i++) { | |||||
uint32_t key = data[i]; | |||||
if (key >= 4 && !contains(key, prev_report)) { | |||||
key_press(data[1], key); | |||||
} | |||||
} | |||||
// Save away the data.. But shift down one byte... Don't need the report number | |||||
memcpy(prev_report, &data[1], 8); | |||||
return true; | |||||
} | |||||
void KeyboardController::release_bluetooth() | |||||
{ | |||||
//btdevice = nullptr; | |||||
} | |||||