325 lines
9.1 KiB
C
325 lines
9.1 KiB
C
#include <stdlib.h>
|
|
|
|
#include "pico/stdlib.h"
|
|
#include "tusb.h"
|
|
#include "hardware/adc.h"
|
|
|
|
#include "hyperx_elite2.h"
|
|
#include "usb_descriptors.h"
|
|
|
|
static bool sending = false;
|
|
static absolute_time_t lastTime;
|
|
static bool backlight = false;
|
|
|
|
static const unsigned int SKIP_INDICES[] = { 23, 29, 41, 47, 70, 71, 76, 87, 88, 93, 99, 100, 102, 108, 113 };
|
|
|
|
static unsigned char buf[BUF_SIZE];
|
|
static uint8_t buf_idx=0;
|
|
static uint8_t packets_sent=0;
|
|
static uint8_t color_idx = 0;
|
|
static uint8_t skipped = 0;
|
|
const unsigned int* skip_idx = &SKIP_INDICES[0];
|
|
|
|
static unsigned char nkro_buf[NKRO_BUF_SIZE];
|
|
static unsigned char nkro_buf1[NKRO_BUF_SIZE];
|
|
static unsigned char nkro_buf2[NKRO_BUF_SIZE];
|
|
static unsigned char mediakeys[1] = {0};
|
|
static unsigned char key_buf[KEY_BUF_SIZE];
|
|
static uint8_t buf_start=0;
|
|
static uint8_t buf_end=0;
|
|
static uint8_t mediabit=0;
|
|
static uint8_t pos=0;
|
|
static bool mute=false;
|
|
static bool sendkeys=false;
|
|
static bool sendmedia=false;
|
|
|
|
void get_light() {
|
|
// get ADC reading from LDR
|
|
// if above threshold, set backlight to off
|
|
if (adc_read() > 400) {
|
|
backlight = false;
|
|
} else {
|
|
backlight = true;
|
|
}
|
|
}
|
|
|
|
void rgb_task(uint8_t dev_addr, uint8_t instance, uint8_t report_id) {
|
|
// the RGB protocol used by HyperX sends individual key RGB data in
|
|
// multiple packets
|
|
// the code here will determine if we are in the middle of sending
|
|
// updated color info (sending==true) and continue to send the next
|
|
// packet if so at a rate of one packet every 20ms
|
|
// otherwise, wait 1s before sending the next set of color packets
|
|
if (sending) {
|
|
if ( absolute_time_diff_us(lastTime, get_absolute_time()) > 20000) {
|
|
if( backlight) {
|
|
send_color(dev_addr, instance, report_id, 0x20, 0x20, 0x20);
|
|
// send a dim white color (#202020) for all keys
|
|
} else{
|
|
send_color(dev_addr, instance, report_id, 0x00, 0x00, 0x00);
|
|
// turn off all lighting by sending (#000000) for all keys
|
|
}
|
|
lastTime = get_absolute_time();
|
|
}
|
|
} else {
|
|
if ( absolute_time_diff_us(lastTime, get_absolute_time()) > 500000) {
|
|
send_initial(dev_addr, instance, report_id);
|
|
lastTime = get_absolute_time();
|
|
}
|
|
}
|
|
}
|
|
|
|
// send an individual color packett with the desired RGB color
|
|
void send_color(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t red, uint8_t green, uint8_t blue) {
|
|
memset(buf, 0x00, BUF_SIZE);
|
|
|
|
buf_idx = 0;
|
|
|
|
// check if there are still keys left to send and send updated colors
|
|
// for those keys
|
|
// each key gets 4 bytes - an init byte (0x81) plus 3 bytes for RGB
|
|
if(color_idx < NUM_KEYS)
|
|
{
|
|
while(color_idx < NUM_KEYS && buf_idx < BUF_SIZE){
|
|
if(*skip_idx == color_idx + skipped)
|
|
{
|
|
// keys in skip_idx are not assigned to a key, so send all 0x00
|
|
buf[buf_idx] = 0x00;
|
|
buf[buf_idx + 1] = 0x00;
|
|
buf[buf_idx + 2] = 0x00;
|
|
buf[buf_idx + 3] = 0x00;
|
|
|
|
skip_idx++;
|
|
|
|
if(skip_idx >= SKIP_INDICES + sizeof(SKIP_INDICES) / sizeof(unsigned int))
|
|
{
|
|
skip_idx = SKIP_INDICES;
|
|
}
|
|
skipped++;
|
|
} else {
|
|
// start by sending color init byte for the current key
|
|
buf[buf_idx] = 0x81;
|
|
// turn rewind, play, and fast forward keys to white
|
|
if(color_idx==105 || color_idx==108 || color_idx==109) {
|
|
buf[buf_idx + 1] = 0x20;
|
|
buf[buf_idx + 2] = 0x20;
|
|
buf[buf_idx + 3] = 0x20;
|
|
} else if(color_idx==99) {
|
|
// set color of mute button to green or red based on mute
|
|
// toggle - note that this toggle is not synced to the PC's
|
|
// audio state and is presumed to be unmuted when the keyboard
|
|
// first receives power
|
|
if(mute) {
|
|
buf[buf_idx + 1] = 0x40;
|
|
buf[buf_idx + 2] = 0x00;
|
|
buf[buf_idx + 3] = 0x00;
|
|
} else {
|
|
buf[buf_idx + 1] = 0x00;
|
|
buf[buf_idx + 2] = 0x40;
|
|
buf[buf_idx + 3] = 0x00;
|
|
}
|
|
} else {
|
|
// for a normal key, send the desired RGB colors
|
|
buf[buf_idx + 1] = red;
|
|
buf[buf_idx + 2] = green;
|
|
buf[buf_idx + 3] = blue;
|
|
}
|
|
color_idx++;
|
|
}
|
|
buf_idx += 4;
|
|
}
|
|
|
|
if(tuh_hid_set_report(dev_addr, instance, report_id, HID_REPORT_TYPE_FEATURE, buf, BUF_SIZE))
|
|
{
|
|
// packet sent successfully, increment
|
|
packets_sent++;
|
|
}
|
|
} else {
|
|
if(packets_sent < NUM_PACKETS)
|
|
{
|
|
// all keys have been sent, but the protocol expects NUM_PACKETS
|
|
// packets to be sent in total; if we have not sent enough packets,
|
|
// send extra packets of all 0x00 until done
|
|
if(tuh_hid_set_report(dev_addr, instance, report_id, HID_REPORT_TYPE_FEATURE, buf, BUF_SIZE))
|
|
{
|
|
packets_sent++;
|
|
}
|
|
} else {
|
|
// mark that sending of a full round of color packets completed
|
|
sending=false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// send the special initialization packet that tells the keyboard to expect
|
|
// color packets to follow
|
|
void send_initial(uint8_t dev_addr, uint8_t instance, uint8_t report_id) {
|
|
memset(buf, 0x00, BUF_SIZE);
|
|
|
|
color_idx = 0;
|
|
skipped = 0;
|
|
skip_idx = &SKIP_INDICES[0];
|
|
|
|
// send initialization packet
|
|
buf[0x00] = 0x04;
|
|
buf[0x01] = 0xf2;
|
|
|
|
if (tuh_hid_set_report(dev_addr, instance, report_id, HID_REPORT_TYPE_FEATURE, buf, BUF_SIZE))
|
|
{
|
|
// we have begun sending packets, so set flag to continue sending
|
|
// packets at regular intervals
|
|
sending = true;
|
|
packets_sent=1;
|
|
}
|
|
}
|
|
|
|
// initialize the ADC for reading the LDR
|
|
void startADC() {
|
|
stdio_init_all();
|
|
adc_init();
|
|
adc_gpio_init(28);
|
|
adc_select_input(2);
|
|
}
|
|
|
|
// process media key presses and prepare HID Consumer Control byte
|
|
void send_media(uint8_t const* report, uint16_t len) {
|
|
(void) len;
|
|
if ( report[0]==0x05 ) {
|
|
switch(report[2]) {
|
|
case 0xB0: // next
|
|
mediabit = 0;
|
|
break;
|
|
case 0xB1: // prev
|
|
mediabit = 1;
|
|
break;
|
|
case 0xB3: // play/pause
|
|
mediabit = 2;
|
|
break;
|
|
case 0xB5: // mute
|
|
mediabit = 3;
|
|
if (report[3]) {
|
|
mute = !mute;
|
|
}
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
if (report[3]) {
|
|
SET_KEYBIT(mediakeys, mediabit);
|
|
} else {
|
|
CLEAR_KEYBIT(mediakeys, mediabit);
|
|
}
|
|
// set flag to send media keys during the next sending round
|
|
sendmedia = true;
|
|
} else if ( report[0]==0x03 ) {
|
|
switch(report[1]) {
|
|
case 0xE9: // vol up
|
|
SET_KEYBIT(mediakeys, 4);
|
|
break;
|
|
case 0xEA: // vol down
|
|
SET_KEYBIT(mediakeys, 5);
|
|
break;
|
|
case 0x00: // none
|
|
CLEAR_KEYBIT(mediakeys, 4);
|
|
CLEAR_KEYBIT(mediakeys, 5);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
sendmedia = true;
|
|
}
|
|
}
|
|
|
|
// process new HID reports from the keyboard and set them up to forward to host
|
|
void process_report(uint8_t instance, uint8_t const* report, uint16_t len) {
|
|
if (instance==HYPERX_ITF_KEYBOARD || instance==HYPERX_ITF_NKRO) {
|
|
// received regular keyboard key events
|
|
if (sendkeys) {
|
|
// keyboard is currently sending to the host, add to queue
|
|
key_buf[buf_end]=len;
|
|
key_buf[buf_end+2]=instance;
|
|
memcpy(&key_buf[buf_end+2], report, len);
|
|
buf_end += (len + 2);
|
|
} else {
|
|
// immediately process keys and send new HID report to host
|
|
updatekeys(instance, report, len);
|
|
}
|
|
} else if (instance==HYPERX_ITF_KEYPRESS) {
|
|
// received media key events
|
|
send_media(report, len);
|
|
}
|
|
}
|
|
|
|
// merge key states from boot keyboard and NKRO keyboard interfaces into a
|
|
// single packet to send to the host
|
|
void updatekeys(uint8_t instance, uint8_t const* report, uint16_t len) {
|
|
if(instance==HYPERX_ITF_KEYBOARD) {
|
|
boot2nkro(report, nkro_buf1, len);
|
|
sendkeys=true;
|
|
} else if (instance==HYPERX_ITF_NKRO) {
|
|
memcpy(nkro_buf2, report, len);
|
|
sendkeys=true;
|
|
}
|
|
merge_bitmap(nkro_buf, nkro_buf1, nkro_buf2);
|
|
}
|
|
|
|
// set initial state of keyboard
|
|
void reset_keyboard() {
|
|
memset(nkro_buf, 0x00, NKRO_BUF_SIZE);
|
|
memset(nkro_buf1, 0x00, NKRO_BUF_SIZE);
|
|
memset(nkro_buf2, 0x00, NKRO_BUF_SIZE);
|
|
memset(key_buf, 0x00, KEY_BUF_SIZE);
|
|
buf_start=0;
|
|
buf_end=0;
|
|
sendkeys=false;
|
|
sendmedia=false;
|
|
}
|
|
|
|
// check if there are any key events waiting to be sent to the host and send
|
|
void keyboard_task() {
|
|
if (sendkeys) {
|
|
// send the current keyboard state to the host
|
|
if (tud_hid_report(REPORT_ID_KEYBOARD, nkro_buf, NKRO_BUF_SIZE)) {
|
|
sendkeys=false;
|
|
}
|
|
} else if (sendmedia) {
|
|
// send the media keys state to the the host
|
|
if (tud_hid_report(REPORT_ID_CONSUMER_CONTROL, mediakeys, 1)) {
|
|
sendmedia=false;
|
|
}
|
|
} else if (buf_start != buf_end) {
|
|
// previous key state has been send, but a new key state is waiting
|
|
// in the buffer
|
|
// load from buffer and setup for sending in the next round
|
|
updatekeys(key_buf[buf_start+1], &key_buf[buf_start+2], key_buf[buf_start]);
|
|
buf_start += (key_buf[buf_start] + 2);
|
|
} else if (buf_start == buf_end) {
|
|
// the buffer has been cleared, so reset the buffer position to front
|
|
buf_start = 0;
|
|
buf_end = 0;
|
|
}
|
|
}
|
|
|
|
// convert a boot keyboard packet into the NKRO bitmap format used by HyperX
|
|
void boot2nkro(uint8_t const* boot_report, uint8_t* nkro_report, uint16_t len) {
|
|
memset(nkro_report, 0x00, NKRO_BUF_SIZE);
|
|
(void) len;
|
|
// copy modifiers
|
|
nkro_report[0] = boot_report[0];
|
|
|
|
// set regular keyboard keys
|
|
for (pos=2; pos<8; pos++) {
|
|
if (boot_report[pos] > 0) {
|
|
SET_KEYBIT(nkro_report, boot_report[pos]+8);
|
|
}
|
|
}
|
|
}
|
|
|
|
// merge two NKRO keyboard bitmaps into a single NKRO keyboard bitmap
|
|
void merge_bitmap(uint8_t* nkro_report, uint8_t const* nkro_report1, uint8_t const* nkro_report2){
|
|
for (pos=0; pos < NKRO_BUF_SIZE; pos++) {
|
|
nkro_report[pos] = nkro_report1[pos] | nkro_report2[pos];
|
|
}
|
|
}
|
|
|