myriad.c 10.0 KB

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