Added connection management

master
Mark 2022-07-08 16:33:42 -07:00
parent fe00a30268
commit 4aa558eda7
Signed by: Mark
GPG Key ID: AD62BB059C2AAEE4
4 changed files with 141 additions and 52 deletions

View File

@ -1,8 +1,20 @@
#pragma once #pragma once
// Sent by host when connection is initiated. // Sent by host when connection is initiated.
//
// Packet structure:
// Data: | cmd |
// # of Bytes: | 1 |
#define CMD_HELLO 0x00 #define CMD_HELLO 0x00
// Sent periodically by host to test connection.
// Keyboard should ignore this command.
//
// Packet structure:
// Data: | cmd |
// # of Bytes: | 1 |
#define CMD_RUTHERE 0x01
// Send keyboard state to host. // Send keyboard state to host.
// //
// Packet structure: // Packet structure:
@ -13,7 +25,7 @@
// 0x00: RGBMatrix disabled // 0x00: RGBMatrix disabled
// 0x01: normal animation, no HID data. // 0x01: normal animation, no HID data.
// 0x02: FFT Animation // 0x02: FFT Animation
#define CMD_SEND_STATE 0x01 #define CMD_SEND_STATE 0x02
// Animation data. Sent by host. // Animation data. Sent by host.
@ -27,7 +39,7 @@
// //
// data: // data:
// Animation data. Content depends on data type. // Animation data. Content depends on data type.
#define CMD_ANIM_DATA 0x02 #define CMD_ANIM_DATA 0x03
// Data for FFT animation. // Data for FFT animation.
// Data segment consists of 10 bits, each representing the height of a column. // Data segment consists of 10 bits, each representing the height of a column.

View File

@ -11,10 +11,10 @@ Ergodox::Ergodox(
product_id(product_id), product_id(product_id),
usage(usage), usage(usage),
usage_page(usage_page), usage_page(usage_page),
handle(NULL) handle(NULL),
connected(false)
{ {
hid_init(); hid_init();
open();
} }
@ -54,21 +54,23 @@ Ergodox& Ergodox::init(
// Instance is a static function variable, // Instance is a static function variable,
// and will be deleted at end of program. // and will be deleted at end of program.
Ergodox::~Ergodox() { Ergodox::~Ergodox() {
close(); disconnect();
hid_exit(); hid_exit();
} }
/* /*
Try to open this keyboard's hid device. Try to open this keyboard's hid device.
Throws a runtime error if device is already open, Throws a runtime error if device is already open.
or if we can't find a device with the given params.
If we successfully open a device, this method sends a If we successfully open a device, this method sends a
CMD_HELLO packet. CMD_HELLO packet.
@returns True if connection was successful, false otherwise.
*/ */
void Ergodox::open() { bool Ergodox::try_connect() {
if (handle != NULL) { if (connected) {
throw std::runtime_error("Device already open"); throw std::runtime_error("Device already open");
} }
@ -89,24 +91,47 @@ void Ergodox::open() {
cur_dev = cur_dev->next; cur_dev = cur_dev->next;
} }
// Return false if connection failed
if (path_to_open) { if (path_to_open) {
connected = true;
handle = hid_open_path(path_to_open); handle = hid_open_path(path_to_open);
hid_set_nonblocking(handle, 1);
hid_free_enumeration(devs); hid_free_enumeration(devs);
write(CMD_HELLO, NULL, 0);
#define MAX_STR 255
wchar_t wstr[MAX_STR];
wprintf(L"\nConnected to device!\n");
// Read metadata
wstr[0] = 0x0000;
hid_get_manufacturer_string(handle, wstr, MAX_STR);
wprintf(L"Manufacturer String: %ls\n", wstr);
wstr[0] = 0x0000;
hid_get_product_string(handle, wstr, MAX_STR);
wprintf(L"Product String: %ls\n", wstr);
} else { } else {
connected = false;
hid_free_enumeration(devs); hid_free_enumeration(devs);
throw std::runtime_error("Could not open hid device"); return false;
} }
hid_set_nonblocking(handle, 1); return connected;
write(CMD_HELLO, NULL, 0);
} }
// Close hid device if it is open. // Close hid device if it is open.
void Ergodox::close() { void Ergodox::disconnect() {
connected = false;
if (handle != NULL) { if (handle != NULL) {
hid_close(handle); hid_close(handle);
handle = NULL;
} }
} }
@ -116,14 +141,15 @@ Send data to Ergodox.
@param cmd Command byte @param cmd Command byte
@param data Data to send @param data Data to send
@returns data_len Data length. Must be shorter than packet_size. @param data_len Data length. Must be shorter than packet_size.
@returns True if successful, false otherwise.
*/ */
int Ergodox::write(uint8_t cmd, const uint8_t* data, uint8_t data_len) const { bool Ergodox::write(uint8_t cmd, const uint8_t* data, uint8_t data_len) {
if (handle == NULL) { if (!connected) {
throw std::runtime_error("Cannot write, device is not open!"); throw std::runtime_error("Not connected, cannot write!");
} } else if (handle == NULL) {
throw std::runtime_error("Tried to write a null handle, something is very wrong.");
if (data_len > packet_size) { } else if (data_len > packet_size) {
throw std::runtime_error("Data length exceeds packet size"); throw std::runtime_error("Data length exceeds packet size");
} }
@ -138,7 +164,15 @@ int Ergodox::write(uint8_t cmd, const uint8_t* data, uint8_t data_len) const {
// Copy data into rest of packet // Copy data into rest of packet
std::copy(data, data + data_len, packet + 2); std::copy(data, data + data_len, packet + 2);
return hid_write(handle, packet, packet_size + 1); ssize_t res;
res = hid_write(handle, packet, packet_size + 1);
if (res < 0) {
wprintf(L"Lost device, disconnecting.\n");
disconnect();
return false;
}
return true;
} }
@ -148,16 +182,21 @@ Read data from Ergodox into read_buf. If a CMD_SEND_STATE packet is received, pr
@returns True if a new command was received, false otherwise. @returns True if a new command was received, false otherwise.
*/ */
bool Ergodox::read() { bool Ergodox::read() {
if (handle == NULL) { if (!connected) {
throw std::runtime_error("Cannot write, device is not open!"); throw std::runtime_error("Not connected, cannot read!");
} else if (handle == NULL) {
throw std::runtime_error("Tried to read a null handle, something is very wrong.");
} }
ssize_t res; ssize_t res;
res = hid_read(handle, read_buf, packet_size); res = hid_read(handle, read_buf, packet_size);
if (res == 0) { if (res == 0) {
return false; return false;
} else if (res < 0) { } else if (res < 0) {
throw std::runtime_error("Read failed."); wprintf(L"Lost device, disconnecting.\n");
disconnect();
return false;
} }
switch(read_buf[0]) { switch(read_buf[0]) {
@ -173,5 +212,11 @@ bool Ergodox::read() {
return false; return false;
} }
return res > 0; return true;
}
// Simple connectivity check
void Ergodox::test_connection() {
write(CMD_RUTHERE, NULL, 0);
} }

View File

@ -32,15 +32,22 @@ class Ergodox {
~Ergodox(); ~Ergodox();
int write(uint8_t cmd, const uint8_t* data, uint8_t data_len) const; bool try_connect();
void disconnect();
void test_connection();
bool read(); bool read();
bool write(uint8_t cmd, const uint8_t* data, uint8_t data_len);
// Read buffer, len = packet_size. // Read buffer, len = packet_size.
// Filled by read(). // Filled by read().
uint8_t read_buf[RAW_EPSIZE]; uint8_t read_buf[RAW_EPSIZE];
// Getter methods
uint8_t get_animation_mode() const { return animation_mode; } uint8_t get_animation_mode() const { return animation_mode; }
bool is_connected() const { return connected; }
private: private:
Ergodox( Ergodox(
@ -55,14 +62,16 @@ class Ergodox {
//Ergodox(Ergodox& other); //Ergodox(Ergodox& other);
//Ergodox& operator=(Ergodox& other); //Ergodox& operator=(Ergodox& other);
void open();
void close();
// HID device. // HID device.
// NULL if not opened. // NULL if not opened.
hid_device* handle; hid_device* handle;
// Keyboard state variables. // Keyboard state variables.
// Updated by read(). // Updated by read().
// Which animation is the keyboard running right now?
// See CMD_SEND_STATE in commands.h for docs.
uint8_t animation_mode; uint8_t animation_mode;
// Are we connected to a keyboard right now?
bool connected;
}; };

View File

@ -5,6 +5,9 @@
// For reading FIFO // For reading FIFO
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
// For sleep
#include <chrono>
#include <thread>
// Local files // Local files
#include "utility/bitmap.hpp" #include "utility/bitmap.hpp"
@ -82,11 +85,18 @@ int main(int argc, char *argv[]) {
); );
// Write buffer // Write buffer
uint8_t hid_buf[12]; uint8_t hid_buf[Dox.packet_size];
wprintf(L"Trying to connect...\n");
while (!Dox.is_connected()) {
Dox.try_connect();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
while (1) { while (1) {
if (Dox.is_connected()) {
if (Dox.get_animation_mode() == 0x02) { if (Dox.get_animation_mode() == 0x02) {
buf.update(); buf.update();
fft.update(buf); fft.update(buf);
@ -103,9 +113,15 @@ int main(int argc, char *argv[]) {
hid_buf[i + 1] = h; hid_buf[i + 1] = h;
} }
Dox.write(CMD_ANIM_DATA, hid_buf, 13);
} }
Dox.write(CMD_ANIM_DATA, hid_buf, Dox.packet_size);
// Dox.write might detect that we've been disconnected,
// and Dox.read will fail if we are.
// This check prevents it from doing that, and instead jumps to reconnect.
if (!Dox.is_connected()) { continue; }
// Read a packet if there is a packet to read // Read a packet if there is a packet to read
if (Dox.read()) { if (Dox.read()) {
uint8_t cmd = Dox.read_buf[0]; uint8_t cmd = Dox.read_buf[0];
@ -115,6 +131,13 @@ int main(int argc, char *argv[]) {
break; break;
} }
} }
} else {
wprintf(L"Trying to connect...\n");
while (!Dox.is_connected()) {
Dox.try_connect();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
} }
return 0; return 0;