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; | |||||
| } | |||||