logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

myriad.c (9981B)


  1. // Copyright 2024 splitkb.com (support@splitkb.com)
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include QMK_KEYBOARD_H
  4. #include "myriad.h"
  5. #include "i2c_master.h"
  6. #include "analog.h"
  7. typedef struct __attribute__((__packed__)) {
  8. char magic_numbers[3];
  9. uint8_t version_major;
  10. uint8_t version_minor;
  11. uint8_t version_patch;
  12. uint32_t checksum;
  13. uint16_t payload_length;
  14. } myriad_header_t;
  15. typedef struct __attribute__((__packed__)) {
  16. uint16_t vendor_id;
  17. uint16_t product_id;
  18. uint8_t revision;
  19. } identity_record_t;
  20. static bool myriad_reader(uint8_t *data, uint16_t length) {
  21. const uint8_t eeprom_address = 0x50; // 1010 000 - NOT shifted for R/W bit
  22. const uint16_t i2c_timeout = 100; // in milliseconds
  23. uint8_t num_pages = (length / 256) + 1;
  24. uint8_t last_page_size = length % 256;
  25. for (int i = 0; i < num_pages; i++) {
  26. uint8_t reg = 0; // We always start on a page boundary, so this is always zero
  27. uint16_t read_length;
  28. if (i == num_pages - 1) {
  29. read_length = last_page_size;
  30. } else {
  31. read_length = 256;
  32. }
  33. i2c_status_t s = i2c_read_register((eeprom_address + i) << 1, reg, &(data[i*256]), read_length, i2c_timeout);
  34. if (s != I2C_STATUS_SUCCESS) { return false; }
  35. }
  36. return true;
  37. }
  38. static bool verify_header(myriad_header_t *header) {
  39. char magic_numbers[] = {'M', 'Y', 'R'};
  40. uint8_t version_major = 1;
  41. uint16_t version_minor = 0;
  42. for (int i = 0; i < sizeof(magic_numbers); i++) {
  43. // Check that the header starts with 'MYR', indicating that this is indeed a Myriad card.
  44. if (header->magic_numbers[i] != magic_numbers[i]) {
  45. return false;
  46. }
  47. }
  48. if (header->version_major != version_major || header->version_minor > version_minor) {
  49. // We obviously don't support cards with a different major version, because that indicates a breaking change.
  50. // We also don't support cards with HIGHER minor version,
  51. // as we are not guaranteed to be able to properly use all its features.
  52. return false;
  53. }
  54. if (header->payload_length > (2048 - sizeof(myriad_header_t))) {
  55. // The EEPROM chips are *at most* 16kb / 2kB large,
  56. // and some of that is taken up by the header.
  57. // We obviously can't have a payload which exceeds the EEPROM's size.
  58. return false;
  59. }
  60. return true;
  61. }
  62. // Sourced from https://en.wikipedia.org/wiki/Adler-32#Example_implementation
  63. static bool verify_checksum(uint8_t *data, uint16_t length, uint32_t checksum) {
  64. // Skip the header
  65. data += sizeof(myriad_header_t);
  66. length -= sizeof(myriad_header_t);
  67. const uint32_t MOD_ADLER = 65521;
  68. uint32_t a = 1, b = 0;
  69. size_t index;
  70. // Process each byte of the data in order
  71. for (index = 0; index < length; ++index)
  72. {
  73. a = (a + data[index]) % MOD_ADLER;
  74. b = (b + a) % MOD_ADLER;
  75. }
  76. uint32_t calculated = ((b << 16) | a);
  77. return calculated == checksum;
  78. }
  79. // Locates a specific entry by type
  80. // Returns the offset of the PAYLOAD.
  81. static int16_t locate_entry(uint8_t entry_type, uint8_t entry_data_length, uint8_t *data, uint16_t minimum, uint16_t maximum) {
  82. if (minimum < sizeof(myriad_header_t)) {
  83. // Records must start *after* the header.
  84. // We silently allow this so the caller can just specify `0` as minimum for the first entry.
  85. minimum = sizeof(myriad_header_t);
  86. }
  87. uint16_t offset = minimum;
  88. while (offset < maximum) {
  89. if (data[offset] == entry_type) {
  90. // Type matches!
  91. if (data[offset+1] == entry_data_length) {
  92. // We found what we are looking for, so return payload reference.
  93. return offset+2;
  94. } else {
  95. // The entry is the wrong length?
  96. return -2;
  97. }
  98. } else {
  99. // No type match, so skip this one
  100. // We skip the type byte, the length byte, and any potential data (with length stored in the length byte)
  101. offset += 2 + data[offset+1];
  102. }
  103. }
  104. // We hit the maximum and didn't find what we are looking for
  105. return -1;
  106. }
  107. static bool read_card_identity(uint8_t *data, uint16_t length, identity_record_t *record) {
  108. const uint8_t identity_type = 0x01;
  109. const uint8_t entry_data_length = sizeof(identity_record_t);
  110. int16_t result = locate_entry(identity_type, entry_data_length, data, 0, length);
  111. if (result < 0) { return false; }
  112. for (int i = 0; i < sizeof(identity_record_t); i++) {
  113. ((uint8_t*)record)[i] = data[result + i];
  114. }
  115. return true;
  116. }
  117. static myriad_card_t _detect_myriad(void) {
  118. gpio_set_pin_input(MYRIAD_PRESENT);
  119. wait_ms(100);
  120. // The pin has an external pull-up, and a Myriad card shorts it to ground.
  121. #ifndef MYRIAD_OVERRIDE_PRESENCE
  122. if (gpio_read_pin(MYRIAD_PRESENT)) {
  123. return NONE;
  124. }
  125. #endif
  126. // Attempt to read header
  127. myriad_header_t header;
  128. if (!myriad_reader((uint8_t*)&header, sizeof(header))) { return INVALID; }
  129. if (!verify_header(&header)) { return INVALID; }
  130. // Now that we have determined that the header is valid
  131. // and we know the payload length, read the entire thing
  132. uint8_t data[2048]; // Guaranteed to be large enough.
  133. uint16_t data_size = sizeof(header)+header.payload_length;
  134. if (!myriad_reader(data, data_size)) { return INVALID; }
  135. if (!verify_checksum(data, data_size, header.checksum)) { return INVALID; }
  136. identity_record_t identity;
  137. if (!read_card_identity(data, data_size, &identity)) { return INVALID; }
  138. if (identity.vendor_id == 0x0001 && identity.product_id == 0x0001) {
  139. return SKB_ENCODER;
  140. } else if (identity.vendor_id == 0x0001 && identity.product_id == 0x0002) {
  141. return SKB_JOYSTICK;
  142. } else if (identity.vendor_id == 0x0001 && identity.product_id == 0x0003) {
  143. return SKB_SWITCHES;
  144. }
  145. return UNKNOWN;
  146. }
  147. // Determine card presence & identity
  148. // Does NOT initialize the card for use!
  149. myriad_card_t detect_myriad(void) {
  150. static myriad_card_t card = UNINITIALIZED;
  151. if (card == UNINITIALIZED) {
  152. i2c_init();
  153. card = _detect_myriad();
  154. }
  155. return card;
  156. }
  157. static void myr_switches_init(void) {
  158. gpio_set_pin_input_high(MYRIAD_GPIO1); // S4
  159. gpio_set_pin_input_high(MYRIAD_GPIO2); // S2
  160. gpio_set_pin_input_high(MYRIAD_GPIO3); // S1
  161. gpio_set_pin_input_high(MYRIAD_GPIO4); // S3
  162. }
  163. static void myr_encoder_init(void) {
  164. gpio_set_pin_input_high(MYRIAD_GPIO1); // Press
  165. gpio_set_pin_input_high(MYRIAD_GPIO2); // A
  166. gpio_set_pin_input_high(MYRIAD_GPIO3); // B
  167. }
  168. static uint16_t myr_joystick_timer;
  169. static void myr_joystick_init(void) {
  170. gpio_set_pin_input_high(MYRIAD_GPIO1); // Press
  171. myr_joystick_timer = timer_read();
  172. }
  173. // Make sure any card present is ready for use
  174. static myriad_card_t myriad_card_init(void) {
  175. static bool initialized = false;
  176. myriad_card_t card = detect_myriad();
  177. if (initialized) {
  178. return card;
  179. }
  180. initialized = true;
  181. switch (card) {
  182. case SKB_SWITCHES:
  183. myr_switches_init();
  184. break;
  185. case SKB_ENCODER:
  186. myr_encoder_init();
  187. break;
  188. case SKB_JOYSTICK:
  189. myr_joystick_init();
  190. break;
  191. default:
  192. break;
  193. }
  194. return card;
  195. }
  196. bool myriad_hook_matrix(matrix_row_t current_matrix[]) {
  197. myriad_card_t card = myriad_card_init();
  198. uint8_t word = 0;
  199. if (card == SKB_SWITCHES) {
  200. word |= ((!gpio_read_pin(MYRIAD_GPIO3)) & 1) << 0;
  201. word |= ((!gpio_read_pin(MYRIAD_GPIO2)) & 1) << 1;
  202. word |= ((!gpio_read_pin(MYRIAD_GPIO4)) & 1) << 2;
  203. word |= ((!gpio_read_pin(MYRIAD_GPIO1)) & 1) << 3;
  204. } else if (card == SKB_ENCODER) {
  205. word |= ((!gpio_read_pin(MYRIAD_GPIO1)) & 1) << 4;
  206. } else if (card == SKB_JOYSTICK) {
  207. word |= ((!gpio_read_pin(MYRIAD_GPIO1)) & 1) << 4;
  208. } else {
  209. return false;
  210. }
  211. // 5 bytes of on-board keys, so we are the 6th
  212. bool matrix_has_changed = current_matrix[5] ^ word;
  213. current_matrix[5] = word;
  214. return matrix_has_changed;
  215. }
  216. static pin_t encoders_pad_a[NUM_ENCODERS_MAX_PER_SIDE];
  217. static pin_t encoders_pad_b[NUM_ENCODERS_MAX_PER_SIDE];
  218. uint8_t myriad_hook_encoder(uint8_t index, bool pad_b) {
  219. if (myriad_card_init() != SKB_ENCODER) { return 0; }
  220. // 3 onboard encoders, so we are number 4
  221. pin_t pin = pad_b ? encoders_pad_b[index] : encoders_pad_a[index];
  222. encoders_pad_a[3] = MYRIAD_GPIO2;
  223. encoders_pad_b[3] = MYRIAD_GPIO3;
  224. return gpio_read_pin(pin) ? 1 : 0;
  225. }
  226. report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report) {
  227. if (myriad_card_init() != SKB_JOYSTICK) { return mouse_report; }
  228. if (timer_elapsed(myr_joystick_timer) < 10) {
  229. wait_ms(2);
  230. return mouse_report;
  231. }
  232. myr_joystick_timer = timer_read();
  233. // `analogReadPin` returns 0..1023
  234. int32_t y = (analogReadPin(MYRIAD_ADC1) - 512) * -1; // Note: axis is flipped
  235. int32_t x = analogReadPin(MYRIAD_ADC2) - 512;
  236. // Values are now -512..512
  237. // Create a dead zone in the middle where the mouse doesn't move
  238. const int16_t dead_zone = 10;
  239. if ((y < 0 && y > -1*dead_zone) || (y > 0 && y < dead_zone)) {
  240. y = 0;
  241. }
  242. if ((x < 0 && x > -1*dead_zone) || (x > 0 && x < dead_zone)) {
  243. x = 0;
  244. }
  245. // quadratic movement
  246. x = abs(x) * x / 5000;
  247. y = abs(y) * y / 5000;
  248. // Clamp final value to make sure we don't under/overflow
  249. if (y < -127) { y = -127; }
  250. if (y > 127) { y = 127; }
  251. if (x < -127) { x = -127; }
  252. if (x > 127) { x = 127; }
  253. mouse_report.x = x;
  254. mouse_report.y = y;
  255. return mouse_report;
  256. }
  257. void pointing_device_driver_init(void) {
  258. gpio_set_pin_input(MYRIAD_ADC1); // Y
  259. gpio_set_pin_input(MYRIAD_ADC2); // X
  260. }