The first gigabyte fix, was to always force the keyboard into boot protocol mode, which appeared to work well for all of the keyboards I tried. Unforationally it caused issues with some wireless keyboard/mouse combo packages, including some logitech and some microsoft keyboards. This version instead, I created a list (currently only one vendor id), of vendor id:product id, that need to be forced into boot mode. Knowing that if there is one keyboard vendor with this N key rollover feature, there are probably others. So while this list can be expanded, this requires users to update library. So added a method forceBootProtocol method to keyboard class that allows the user to try it out without needing to update the library sources. The mouse test program has example of either setting it before the keyboard is initiated, as well as another way that when a connection is found it asks for the vendor id, and if it is the one of my gigabyte keyboard, it calls the function then. While doing this I found some of the query functions like idVendor on the keyboard class would not compile as the keyboard object has multiple inheritence and both base classes have this. So like the joystick objects I added these to the top level function and these functions will direct the calls to the approrpriate places.main
@@ -683,6 +683,13 @@ public: | |||
KeyboardController(USBHost &host) { init(); } | |||
KeyboardController(USBHost *host) { init(); } | |||
// need their own versions as both USBDriver and USBHIDInput provide | |||
uint16_t idVendor(); | |||
uint16_t idProduct(); | |||
const uint8_t *manufacturer(); | |||
const uint8_t *product(); | |||
const uint8_t *serialNumber(); | |||
// Some methods are in both public classes so we need to figure out which one to use | |||
operator bool() { return (device != nullptr); } | |||
// Main boot keyboard functions. | |||
@@ -704,6 +711,7 @@ public: | |||
// Added for extras information. | |||
void attachExtrasPress(void (*f)(uint32_t top, uint16_t code)) { extrasKeyPressedFunction = f; } | |||
void attachExtrasRelease(void (*f)(uint32_t top, uint16_t code)) { extrasKeyReleasedFunction = f; } | |||
void forceBootProtocol(); | |||
enum {MAX_KEYS_DOWN=4}; | |||
@@ -750,7 +758,8 @@ private: | |||
volatile bool hid_input_data_ = false; // did we receive any valid data with report? | |||
uint8_t count_keys_down_ = 0; | |||
uint16_t keys_down[MAX_KEYS_DOWN]; | |||
bool force_boot_protocol; // User or VID/PID said force boot protocol? | |||
bool control_queued; | |||
}; | |||
@@ -1283,7 +1292,7 @@ private: | |||
uint8_t pl2303_v1; // Which version do we have | |||
uint8_t pl2303_v2; | |||
uint8_t interface; | |||
bool control_queued; | |||
bool control_queued; // Is there already a queued control messaged | |||
typedef enum { UNKNOWN=0, CDCACM, FTDI, PL2303, CH341, CP210X } sertype_t; | |||
sertype_t sertype; | |||
@@ -21,21 +21,21 @@ uint32_t buttons_prev = 0; | |||
RawHIDController rawhid1(myusb); | |||
RawHIDController rawhid2(myusb, 0xffc90004); | |||
USBDriver *drivers[] = {&hub1, &hub2,&keyboard1, &keyboard2, &joystick1, &hid1, &hid2, &hid3, &hid4, &hid5}; | |||
USBDriver *drivers[] = {&hub1, &hub2, &keyboard1, &keyboard2, &joystick1, &hid1, &hid2, &hid3, &hid4, &hid5}; | |||
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0])) | |||
const char * driver_names[CNT_DEVICES] = {"Hub1","Hub2", "KB1", "KB2", "JOY1D", "HID1", "HID2", "HID3", "HID4", "HID5"}; | |||
const char * driver_names[CNT_DEVICES] = {"Hub1", "Hub2", "KB1", "KB2", "JOY1D", "HID1", "HID2", "HID3", "HID4", "HID5"}; | |||
bool driver_active[CNT_DEVICES] = {false, false, false, false}; | |||
// Lets also look at HID Input devices | |||
USBHIDInput *hiddrivers[] = {&mouse1, &joystick1, &rawhid1, &rawhid2}; | |||
#define CNT_HIDDEVICES (sizeof(hiddrivers)/sizeof(hiddrivers[0])) | |||
const char * hid_driver_names[CNT_DEVICES] = {"Mouse1","Joystick1", "RawHid1", "RawHid2"}; | |||
const char * hid_driver_names[CNT_DEVICES] = {"Mouse1", "Joystick1", "RawHid1", "RawHid2"}; | |||
bool hid_driver_active[CNT_DEVICES] = {false, false}; | |||
bool show_changed_only = false; | |||
bool show_changed_only = false; | |||
uint8_t joystick_left_trigger_value = 0; | |||
uint8_t joystick_right_trigger_value = 0; | |||
uint64_t joystick_full_notify_mask = (uint64_t)-1; | |||
uint64_t joystick_full_notify_mask = (uint64_t) - 1; | |||
void setup() | |||
{ | |||
@@ -50,6 +50,11 @@ void setup() | |||
keyboard2.attachExtrasPress(OnHIDExtrasPress); | |||
keyboard2.attachExtrasRelease(OnHIDExtrasRelease); | |||
// The below forceBootProtocol will force which ever | |||
// next keyboard that attaches to this device to be in boot protocol | |||
// Only try this if you run into keyboard with issues. If this is a combined | |||
// device like wireless mouse and keyboard this can cause mouse problems. | |||
//keyboard1.forceBootProtocol(); | |||
rawhid1.attachReceive(OnReceiveHidData); | |||
rawhid2.attachReceive(OnReceiveHidData); | |||
} | |||
@@ -60,8 +65,8 @@ void loop() | |||
myusb.Task(); | |||
if (Serial.available()) { | |||
int ch = Serial.read(); // get the first char. | |||
while (Serial.read() != -1) ; | |||
int ch = Serial.read(); // get the first char. | |||
while (Serial.read() != -1) ; | |||
if ((ch == 'b') || (ch == 'B')) { | |||
Serial.println("Only notify on Basic Axis changes"); | |||
joystick1.axisChangeNotifyMask(0x3ff); | |||
@@ -78,7 +83,7 @@ void loop() | |||
Serial.println("\n*** Show only changed fields mode ***"); | |||
} | |||
} | |||
} | |||
} | |||
for (uint8_t i = 0; i < CNT_DEVICES; i++) { | |||
if (*drivers[i] != driver_active[i]) { | |||
@@ -95,6 +100,19 @@ void loop() | |||
if (psz && *psz) Serial.printf(" product: %s\n", psz); | |||
psz = drivers[i]->serialNumber(); | |||
if (psz && *psz) Serial.printf(" Serial: %s\n", psz); | |||
// Note: with some keyboards there is an issue that they don't output in boot protocol mode | |||
// and may not work. The above code can try to force the keyboard into boot mode, but there | |||
// are issues with doing this blindly with combo devices like wireless keyboard/mouse, which | |||
// may cause the mouse to not work. Note: the above id is in the builtin list of | |||
// vendor IDs that are already forced | |||
if (drivers[i] == &keyboard1) { | |||
if (keyboard1.idVendor() == 0x04D9) { | |||
Serial.println("Gigabyte vendor: force boot protocol"); | |||
// Gigabyte keyboard | |||
keyboard1.forceBootProtocol(); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
@@ -120,7 +138,7 @@ void loop() | |||
if(mouse1.available()) { | |||
if (mouse1.available()) { | |||
Serial.print("Mouse: buttons = "); | |||
Serial.print(mouse1.getButtons()); | |||
Serial.print(", mouseX = "); | |||
@@ -168,7 +186,7 @@ void loop() | |||
joystick_left_trigger_value = ltv; | |||
joystick_right_trigger_value = rtv; | |||
joystick1.setRumble(ltv, rtv); | |||
} | |||
} | |||
break; | |||
case JoystickController::PS3: | |||
@@ -178,11 +196,11 @@ void loop() | |||
joystick_left_trigger_value = ltv; | |||
joystick_right_trigger_value = rtv; | |||
joystick1.setRumble(ltv, rtv, 50); | |||
} | |||
} | |||
break; | |||
case JoystickController::XBOXONE: | |||
case JoystickController::XBOX360: | |||
case JoystickController::XBOXONE: | |||
case JoystickController::XBOX360: | |||
ltv = joystick1.getAxis(4); | |||
rtv = joystick1.getAxis(5); | |||
if ((ltv != joystick_left_trigger_value) || (rtv != joystick_right_trigger_value)) { | |||
@@ -190,16 +208,16 @@ void loop() | |||
joystick_right_trigger_value = rtv; | |||
joystick1.setRumble(ltv, rtv); | |||
Serial.printf(" Set Rumble %d %d", ltv, rtv); | |||
} | |||
} | |||
break; | |||
} | |||
if (buttons != buttons_prev) { | |||
if (joystick1.joystickType == JoystickController::PS3) { | |||
joystick1.setLEDs((buttons>>12) & 0xf); // try to get to TRI/CIR/X/SQuare | |||
joystick1.setLEDs((buttons >> 12) & 0xf); // try to get to TRI/CIR/X/SQuare | |||
} else { | |||
uint8_t lr = (buttons & 1)? 0xff : 0; | |||
uint8_t lg = (buttons & 2)? 0xff : 0; | |||
uint8_t lb = (buttons & 4)? 0xff : 0; | |||
uint8_t lr = (buttons & 1) ? 0xff : 0; | |||
uint8_t lg = (buttons & 2) ? 0xff : 0; | |||
uint8_t lb = (buttons & 4) ? 0xff : 0; | |||
joystick1.setLEDs(lr, lg, lb); | |||
} | |||
buttons_prev = buttons; | |||
@@ -213,7 +231,7 @@ void loop() | |||
if (rawhid1) { | |||
int ch; | |||
uint8_t buffer[64]; | |||
uint8_t count_chars = 0; | |||
uint8_t count_chars = 0; | |||
memset(buffer, 0, sizeof(buffer)); | |||
if (Serial.available()) { | |||
while (((ch = Serial.read()) != -1) && (count_chars < sizeof(buffer))) { | |||
@@ -277,7 +295,7 @@ void OnPress(int key) | |||
//Serial.print((char)keyboard2.getKey()); | |||
//Serial.println(); | |||
} | |||
void OnHIDExtrasPress(uint32_t top, uint16_t key) | |||
void OnHIDExtrasPress(uint32_t top, uint16_t key) | |||
{ | |||
Serial.print("HID ("); | |||
Serial.print(top, HEX); | |||
@@ -513,7 +531,7 @@ void OnHIDExtrasPress(uint32_t top, uint16_t key) | |||
Serial.println(); | |||
} | |||
void OnHIDExtrasRelease(uint32_t top, uint16_t key) | |||
void OnHIDExtrasRelease(uint32_t top, uint16_t key) | |||
{ | |||
Serial.print("HID ("); | |||
Serial.print(top, HEX); | |||
@@ -523,10 +541,10 @@ void OnHIDExtrasRelease(uint32_t top, uint16_t key) | |||
bool OnReceiveHidData(uint32_t usage, const uint8_t *data, uint32_t len) { | |||
// Called for maybe both HIDS for rawhid basic test. One is for the Teensy | |||
// to output to Serial. while still having Raw Hid... | |||
// to output to Serial. while still having Raw Hid... | |||
if (usage == 0xffc90004) { | |||
// Lets trim off trailing null characters. | |||
while ((len > 0) && (data[len-1] == 0)) { | |||
while ((len > 0) && (data[len - 1] == 0)) { | |||
len--; | |||
} | |||
if (len) { | |||
@@ -537,7 +555,7 @@ bool OnReceiveHidData(uint32_t usage, const uint8_t *data, uint32_t len) { | |||
Serial.print("RawHID data: "); | |||
Serial.println(usage, HEX); | |||
while (len) { | |||
uint8_t cb = (len > 16)? 16 : len; | |||
uint8_t cb = (len > 16) ? 16 : len; | |||
const uint8_t *p = data; | |||
uint8_t i; | |||
for (i = 0; i < cb; i++) { | |||
@@ -545,7 +563,7 @@ bool OnReceiveHidData(uint32_t usage, const uint8_t *data, uint32_t len) { | |||
} | |||
Serial.print(": "); | |||
for (i = 0; i < cb; i++) { | |||
Serial.write(((*data >= ' ')&&(*data <= '~'))? *data : '.'); | |||
Serial.write(((*data >= ' ') && (*data <= '~')) ? *data : '.'); | |||
data++; | |||
} | |||
len -= cb; | |||
@@ -554,4 +572,4 @@ bool OnReceiveHidData(uint32_t usage, const uint8_t *data, uint32_t len) { | |||
} | |||
return true; | |||
} | |||
} |
@@ -36,12 +36,17 @@ typedef struct { | |||
uint8_t charNumlockOn; // We will assume when num lock is on we have all characters... | |||
} keycode_numlock_t; | |||
typedef struct { | |||
uint16_t idVendor; // vendor id of keyboard | |||
uint16_t idProduct; // product id - 0 implies all of the ones from vendor; | |||
} keyboard_force_boot_protocol_t; // list of products to force into boot protocol | |||
#ifdef M | |||
#undef M | |||
#endif | |||
#define M(n) ((n) & KEYCODE_MASK) | |||
keycode_extra_t keycode_extras[] = { | |||
static const keycode_extra_t keycode_extras[] = { | |||
{M(KEY_ENTER), '\n'}, | |||
{M(KEY_ESC), 0x1b}, | |||
{M(KEY_TAB), 0x9 }, | |||
@@ -70,7 +75,7 @@ keycode_extra_t keycode_extras[] = { | |||
}; | |||
// Some of these mapped to key + shift. | |||
keycode_numlock_t keycode_numlock[] = { | |||
static const keycode_numlock_t keycode_numlock[] = { | |||
{M(KEYPAD_SLASH), '/', '/'}, | |||
{M(KEYPAD_ASTERIX), '*', '*'}, | |||
{M(KEYPAD_MINUS), '-', '-'}, | |||
@@ -89,6 +94,11 @@ keycode_numlock_t keycode_numlock[] = { | |||
{M(KEYPAD_PERIOD), 0x80 | M(KEY_DELETE), '.'} | |||
}; | |||
static const keyboard_force_boot_protocol_t keyboard_forceBootMode[] = { | |||
{0x04D9, 0} | |||
}; | |||
#define print USBHost::print_ | |||
#define println USBHost::println_ | |||
@@ -99,6 +109,7 @@ void KeyboardController::init() | |||
contribute_String_Buffers(mystring_bufs, sizeof(mystring_bufs)/sizeof(strbuf_t)); | |||
driver_ready_for_device(this); | |||
USBHIDParser::driver_ready_for_hid_collection(this); | |||
force_boot_protocol = false; // start off assuming not | |||
} | |||
bool KeyboardController::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) | |||
@@ -108,6 +119,7 @@ bool KeyboardController::claim(Device_t *dev, int type, const uint8_t *descripto | |||
// only claim at interface level | |||
if (type != 1) return false; | |||
if (len < 9+9+7) return false; | |||
print_hexbytes(descriptors, len); | |||
uint32_t numendpoint = descriptors[4]; | |||
if (numendpoint < 1) return false; | |||
@@ -139,22 +151,45 @@ bool KeyboardController::claim(Device_t *dev, int type, const uint8_t *descripto | |||
datapipe->callback_function = callback; | |||
queue_Data_Transfer(datapipe, report, 8, this); | |||
mk_setup(setup, 0x21, 11, 0, 0, 0); // 11=SET_PROTOCOL BOOT | |||
// see if this device in list of devices that need to be set in | |||
// boot protocol mode | |||
bool in_forceBoot_mode_list = false; | |||
for (uint8_t i = 0; i < sizeof(keyboard_forceBootMode)/sizeof(keyboard_forceBootMode[0]); i++) { | |||
if (dev->idVendor == keyboard_forceBootMode[i].idVendor) { | |||
if ((dev->idProduct == keyboard_forceBootMode[i].idProduct) || | |||
(keyboard_forceBootMode[i].idProduct == 0)) { | |||
in_forceBoot_mode_list = true; | |||
break; | |||
} | |||
} | |||
} | |||
if (in_forceBoot_mode_list) { | |||
println("SET_PROTOCOL Boot"); | |||
mk_setup(setup, 0x21, 11, 0, 0, 0); // 11=SET_PROTOCOL BOOT | |||
} else { | |||
mk_setup(setup, 0x21, 10, 0, 0, 0); // 10=SET_IDLE | |||
} | |||
queue_Control_Transfer(dev, &setup, NULL, this); | |||
control_queued = true; | |||
return true; | |||
} | |||
void KeyboardController::control(const Transfer_t *transfer) | |||
{ | |||
println("control callback (keyboard)"); | |||
control_queued = false; | |||
print_hexbytes(transfer->buffer, transfer->length); | |||
// To decode hex dump to human readable HID report summary: | |||
// http://eleccelerator.com/usbdescreqparser/ | |||
uint32_t mesg = transfer->setup.word1; | |||
println(" mesg = ", mesg, HEX); | |||
if (mesg == 0x001021 && transfer->length == 0) { // SET_PROTOCOL | |||
if (mesg == 0x00B21 && transfer->length == 0) { // SET_PROTOCOL | |||
mk_setup(setup, 0x21, 10, 0, 0, 0); // 10=SET_IDLE | |||
control_queued = true; | |||
queue_Control_Transfer(device, &setup, NULL, this); | |||
} else if (force_boot_protocol) { | |||
forceBootProtocol(); // lets setup to do the boot protocol | |||
force_boot_protocol = false; // turn back off | |||
} | |||
} | |||
@@ -166,6 +201,17 @@ void KeyboardController::callback(const Transfer_t *transfer) | |||
} | |||
} | |||
void KeyboardController::forceBootProtocol() | |||
{ | |||
if (device && !control_queued) { | |||
mk_setup(setup, 0x21, 11, 0, 0, 0); // 11=SET_PROTOCOL BOOT | |||
control_queued = true; | |||
queue_Control_Transfer(device, &setup, NULL, this); | |||
} else { | |||
force_boot_protocol = true; // let system know we want to force this. | |||
} | |||
} | |||
void KeyboardController::disconnect() | |||
{ | |||
// TODO: free resources | |||
@@ -437,3 +483,43 @@ void KeyboardController::hid_input_end() | |||
hid_input_begin_ = false; | |||
} | |||
} | |||
//***************************************************************************** | |||
// Some simple query functions depend on which interface we are using... | |||
//***************************************************************************** | |||
uint16_t KeyboardController::idVendor() | |||
{ | |||
if (device != nullptr) return device->idVendor; | |||
if (mydevice != nullptr) return mydevice->idVendor; | |||
return 0; | |||
} | |||
uint16_t KeyboardController::idProduct() | |||
{ | |||
if (device != nullptr) return device->idProduct; | |||
if (mydevice != nullptr) return mydevice->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 ((mydevice != nullptr) && (mydevice->strbuf != nullptr)) return &mydevice->strbuf->buffer[mydevice->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 ((mydevice != nullptr) && (mydevice->strbuf != nullptr)) return &mydevice->strbuf->buffer[mydevice->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 ((mydevice != nullptr) && (mydevice->strbuf != nullptr)) return &mydevice->strbuf->buffer[mydevice->strbuf->iStrings[strbuf_t::STR_ID_SERIAL]]; | |||
return nullptr; | |||
} | |||
@@ -31,6 +31,7 @@ updateLEDS KEYWORD2 | |||
numLock KEYWORD2 | |||
capsLock KEYWORD2 | |||
scrollLock KEYWORD2 | |||
forceBootProtocol KEYWORD2 | |||
# MIDIDevice | |||
getType KEYWORD2 |