firmware+controller: Initial working HID state control
This commit is contained in:
		@@ -1,17 +1,22 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					#!/usr/bin/env python3
 | 
				
			||||||
import hid
 | 
					import logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
USB_VID = 0x6969
 | 
					import valconomy
 | 
				
			||||||
USB_PID = 0x0004
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
def main():
 | 
					def main():
 | 
				
			||||||
  hid_handle = hid.device()
 | 
					  logging.basicConfig(
 | 
				
			||||||
  hid_handle.open(vendor_id=USB_VID, product_id=USB_PID)
 | 
					    format='%(asctime)s %(name)s %(levelname)s %(message)s', level=logging.INFO)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  h = valconomy.HIDValconomyHandler()
 | 
				
			||||||
  try:
 | 
					  try:
 | 
				
			||||||
    hid_handle.write(b'\x00test')
 | 
					    h.menu(None, False)
 | 
				
			||||||
 | 
					    h.none()
 | 
				
			||||||
 | 
					    h.menu(None, True)
 | 
				
			||||||
 | 
					    h.idle(None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    h.service()
 | 
				
			||||||
  finally:
 | 
					  finally:
 | 
				
			||||||
    hid_handle.close()
 | 
					    h.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == '__main__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
  main()
 | 
					  main()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,13 @@
 | 
				
			|||||||
import base64
 | 
					import base64
 | 
				
			||||||
from dataclasses import dataclass
 | 
					from dataclasses import dataclass
 | 
				
			||||||
from enum import Enum
 | 
					from enum import Enum
 | 
				
			||||||
 | 
					import errno
 | 
				
			||||||
import json
 | 
					import json
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
from pprint import pprint
 | 
					from pprint import pprint
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
 | 
					import queue
 | 
				
			||||||
 | 
					import struct
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
from typing import Callable
 | 
					from typing import Callable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -49,12 +52,12 @@ class RiotPlayerInfo:
 | 
				
			|||||||
    return f'{self.name}#{self.tag}'
 | 
					    return f'{self.name}#{self.tag}'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ValorantLocalClient:
 | 
					class ValorantLocalClient:
 | 
				
			||||||
  lockfile_path = os.path.join(os.environ['LOCALAPPDATA'], r'Riot Games\Riot Client\Config\lockfile')
 | 
					 | 
				
			||||||
  poll_interval = 0.5
 | 
					  poll_interval = 0.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def __init__(self, callback: Callable[[RiotPlayerInfo, bool], None]):
 | 
					  def __init__(self, callback: Callable[[RiotPlayerInfo, bool], None]):
 | 
				
			||||||
    self.callback = callback
 | 
					    self.callback = callback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    self.lockfile_path = os.path.join(os.environ['LOCALAPPDATA'], r'Riot Games\Riot Client\Config\lockfile')
 | 
				
			||||||
    self.port, self.password = None, None
 | 
					    self.port, self.password = None, None
 | 
				
			||||||
    self.running = True
 | 
					    self.running = True
 | 
				
			||||||
    self.players = {}
 | 
					    self.players = {}
 | 
				
			||||||
@@ -228,7 +231,78 @@ class ValconomyHandler:
 | 
				
			|||||||
      log.info('Hard luck...')
 | 
					      log.info('Hard luck...')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class HIDValconomyHandler(ValconomyHandler):
 | 
					class HIDValconomyHandler(ValconomyHandler):
 | 
				
			||||||
  pass
 | 
					  vid = 0x6969
 | 
				
			||||||
 | 
					  pid = 0x0004
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def __init__(self):
 | 
				
			||||||
 | 
					    self.dev = None
 | 
				
			||||||
 | 
					    self.running = True
 | 
				
			||||||
 | 
					    self.queue = queue.Queue(128)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def close(self):
 | 
				
			||||||
 | 
					    if self.dev is not None:
 | 
				
			||||||
 | 
					      self.dev.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def _dev_ready(self):
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					      if self.dev is None:
 | 
				
			||||||
 | 
					        dev = hid.device()
 | 
				
			||||||
 | 
					        dev.open(vendor_id=self.vid, product_id=self.pid)
 | 
				
			||||||
 | 
					        self.dev = dev
 | 
				
			||||||
 | 
					        log.info(f'USB device opened')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      # 2 bytes: report ID and returned value
 | 
				
			||||||
 | 
					      # We get back the same report ID and value
 | 
				
			||||||
 | 
					      data = self.dev.get_input_report(0, 2)
 | 
				
			||||||
 | 
					      assert len(data) == 2
 | 
				
			||||||
 | 
					      return data[1] == 1
 | 
				
			||||||
 | 
					    except OSError:
 | 
				
			||||||
 | 
					      if self.dev is not None:
 | 
				
			||||||
 | 
					        log.warn(f'USB device lost')
 | 
				
			||||||
 | 
					        self.dev = None
 | 
				
			||||||
 | 
					      return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def _do(self, cmd, *vals, fmt=None):
 | 
				
			||||||
 | 
					    if fmt is None:
 | 
				
			||||||
 | 
					      fmt = ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fmt = '<BB' + fmt
 | 
				
			||||||
 | 
					    # Prepend report ID 0
 | 
				
			||||||
 | 
					    data = struct.pack(fmt, *(0, cmd) + vals)
 | 
				
			||||||
 | 
					    self.dev.write(data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def _enq(self, cmd, *vals, fmt=None):
 | 
				
			||||||
 | 
					    self.queue.put((cmd, vals, fmt))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def service(self):
 | 
				
			||||||
 | 
					    while not self.queue.empty():
 | 
				
			||||||
 | 
					      cmd, vals, fmt = self.queue.get()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      while not self._dev_ready():
 | 
				
			||||||
 | 
					        if not self.running:
 | 
				
			||||||
 | 
					          break
 | 
				
			||||||
 | 
					        time.sleep(0.1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      try:
 | 
				
			||||||
 | 
					        self._do(cmd, *vals, fmt=fmt)
 | 
				
			||||||
 | 
					      except OSError as ex:
 | 
				
			||||||
 | 
					        log.warn(f'USB device lost, state dequeuing stalled')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def run(self):
 | 
				
			||||||
 | 
					    while self.running:
 | 
				
			||||||
 | 
					      while self.queue.empty():
 | 
				
			||||||
 | 
					        time.sleep(0.5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      self.service()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def none(self):
 | 
				
			||||||
 | 
					    self._enq(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def menu(self, info: RiotPlayerInfo, was_idle: bool=False):
 | 
				
			||||||
 | 
					    self._enq(1, 1 if was_idle else 0, fmt='B')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def idle(self, info: RiotPlayerInfo):
 | 
				
			||||||
 | 
					    self._enq(2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class GameState(Enum):
 | 
					class GameState(Enum):
 | 
				
			||||||
  NONE = 0
 | 
					  NONE = 0
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -280,7 +280,5 @@ void val_lvgl_ui(lv_display_t *disp) {
 | 
				
			|||||||
  lv_label_set_text(l_cfg, LV_SYMBOL_SETTINGS);
 | 
					  lv_label_set_text(l_cfg, LV_SYMBOL_SETTINGS);
 | 
				
			||||||
  lv_obj_center(l_cfg);
 | 
					  lv_obj_center(l_cfg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // val_ui_none();
 | 
					  val_ui_none();
 | 
				
			||||||
  // val_ui_menu(true);
 | 
					 | 
				
			||||||
  val_ui_idle();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,7 @@
 | 
				
			|||||||
#include "common.h"
 | 
					#include "common.h"
 | 
				
			||||||
#include "usb.h"
 | 
					#include "usb.h"
 | 
				
			||||||
#include "lcd.h"
 | 
					#include "lcd.h"
 | 
				
			||||||
 | 
					#include "ui.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_INOUT_DESC_LEN)
 | 
					#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_INOUT_DESC_LEN)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -77,24 +78,55 @@ uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance) {
 | 
				
			|||||||
// Invoked when received GET_REPORT control request
 | 
					// Invoked when received GET_REPORT control request
 | 
				
			||||||
// Application must fill buffer report's content and return its length.
 | 
					// Application must fill buffer report's content and return its length.
 | 
				
			||||||
// Return zero will cause the stack to STALL request
 | 
					// Return zero will cause the stack to STALL request
 | 
				
			||||||
uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) {
 | 
					uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buf, uint16_t reqlen) {
 | 
				
			||||||
    (void) instance;
 | 
					    (void) instance;
 | 
				
			||||||
    (void) report_id;
 | 
					    if (report_id != 0 || reqlen < 1) {
 | 
				
			||||||
    (void) report_type;
 | 
					      return 0;
 | 
				
			||||||
    (void) buffer;
 | 
					    }
 | 
				
			||||||
    (void) reqlen;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return 0;
 | 
					    // ESP_LOGI(TAG, "Got %hu bytes report %hhu", reqlen, report_id);
 | 
				
			||||||
 | 
					    // for (uint16_t i = 0; i < reqlen; i++) {
 | 
				
			||||||
 | 
					    //   ESP_LOGI(TAG, "b: %02hhx", buf[i]);
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					    buf[0] = val_ui_state_ready();
 | 
				
			||||||
 | 
					    return 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Invoked when received SET_REPORT control request or
 | 
					// Invoked when received SET_REPORT control request or
 | 
				
			||||||
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
 | 
					// received data on OUT endpoint ( Report ID = 0, Type = 0 )
 | 
				
			||||||
void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) {
 | 
					void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buf, uint16_t bufsize) {
 | 
				
			||||||
  assert(report_id == 0 && report_type == HID_REPORT_TYPE_OUTPUT);
 | 
					  (void) instance;
 | 
				
			||||||
  ESP_LOGI(TAG, "Got %hu bytes report %hhu", bufsize, report_id);
 | 
					  assert(report_id == 0 && report_type == HID_REPORT_TYPE_OUTPUT && bufsize >= 1);
 | 
				
			||||||
  for (uint16_t i = 0; i < bufsize; i++) {
 | 
					  if (!val_lvgl_lock(-1)) {
 | 
				
			||||||
    ESP_LOGI(TAG, "b: %02hhx", buffer[i]);
 | 
					    ESP_LOGE(TAG, "Failed to grab LVGL lock");
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  if (buf[0] > ST_IDLE) {
 | 
				
			||||||
 | 
					    ESP_LOGW(TAG, "Unknown state %hhu", buf[0]);
 | 
				
			||||||
 | 
					    goto ret;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (!val_ui_state_ready()) {
 | 
				
			||||||
 | 
					    goto ret;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  switch (buf[0]) {
 | 
				
			||||||
 | 
					  case ST_NONE:
 | 
				
			||||||
 | 
					    val_ui_none();
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  case ST_MENU:
 | 
				
			||||||
 | 
					    if (bufsize < 2) {
 | 
				
			||||||
 | 
					      ESP_LOGE(TAG, "Invalid ST_MENU command");
 | 
				
			||||||
 | 
					      goto ret;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    val_ui_menu((bool)buf[1]);
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  case ST_IDLE:
 | 
				
			||||||
 | 
					    val_ui_idle();
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ret:
 | 
				
			||||||
 | 
					  val_lvgl_unlock();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void val_usb_init(void) {
 | 
					void val_usb_init(void) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,4 +5,10 @@
 | 
				
			|||||||
#define EPNUM_HID 0x01
 | 
					#define EPNUM_HID 0x01
 | 
				
			||||||
#define USB_EP_BUFSIZE 64
 | 
					#define USB_EP_BUFSIZE 64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum val_state {
 | 
				
			||||||
 | 
					  ST_NONE = 0,
 | 
				
			||||||
 | 
					  ST_MENU,
 | 
				
			||||||
 | 
					  ST_IDLE
 | 
				
			||||||
 | 
					} val_state_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void val_usb_init(void);
 | 
					void val_usb_init(void);
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user