KissModem.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. #include "KissModem.h"
  2. #include <CayenneLPP.h>
  3. KissModem::KissModem(Stream& serial, mesh::LocalIdentity& identity, mesh::RNG& rng,
  4. mesh::Radio& radio, mesh::MainBoard& board, SensorManager& sensors)
  5. : _serial(serial), _identity(identity), _rng(rng), _radio(radio), _board(board), _sensors(sensors) {
  6. _rx_len = 0;
  7. _rx_escaped = false;
  8. _rx_active = false;
  9. _has_pending_tx = false;
  10. _pending_tx_len = 0;
  11. _txdelay = KISS_DEFAULT_TXDELAY;
  12. _persistence = KISS_DEFAULT_PERSISTENCE;
  13. _slottime = KISS_DEFAULT_SLOTTIME;
  14. _txtail = 0;
  15. _fullduplex = 0;
  16. _tx_state = TX_IDLE;
  17. _tx_timer = 0;
  18. _setRadioCallback = nullptr;
  19. _setTxPowerCallback = nullptr;
  20. _getCurrentRssiCallback = nullptr;
  21. _getStatsCallback = nullptr;
  22. _config = {0, 0, 0, 0, 0};
  23. _signal_report_enabled = true;
  24. }
  25. void KissModem::begin() {
  26. _rx_len = 0;
  27. _rx_escaped = false;
  28. _rx_active = false;
  29. _has_pending_tx = false;
  30. _tx_state = TX_IDLE;
  31. }
  32. void KissModem::writeByte(uint8_t b) {
  33. if (b == KISS_FEND) {
  34. _serial.write(KISS_FESC);
  35. _serial.write(KISS_TFEND);
  36. } else if (b == KISS_FESC) {
  37. _serial.write(KISS_FESC);
  38. _serial.write(KISS_TFESC);
  39. } else {
  40. _serial.write(b);
  41. }
  42. }
  43. void KissModem::writeFrame(uint8_t type, const uint8_t* data, uint16_t len) {
  44. _serial.write(KISS_FEND);
  45. writeByte(type);
  46. for (uint16_t i = 0; i < len; i++) {
  47. writeByte(data[i]);
  48. }
  49. _serial.write(KISS_FEND);
  50. }
  51. void KissModem::writeHardwareFrame(uint8_t sub_cmd, const uint8_t* data, uint16_t len) {
  52. _serial.write(KISS_FEND);
  53. writeByte(KISS_CMD_SETHARDWARE);
  54. writeByte(sub_cmd);
  55. for (uint16_t i = 0; i < len; i++) {
  56. writeByte(data[i]);
  57. }
  58. _serial.write(KISS_FEND);
  59. }
  60. void KissModem::writeHardwareError(uint8_t error_code) {
  61. writeHardwareFrame(HW_RESP_ERROR, &error_code, 1);
  62. }
  63. void KissModem::loop() {
  64. while (_serial.available()) {
  65. uint8_t b = _serial.read();
  66. if (b == KISS_FEND) {
  67. if (_rx_active && _rx_len > 0) {
  68. processFrame();
  69. }
  70. _rx_len = 0;
  71. _rx_escaped = false;
  72. _rx_active = true;
  73. continue;
  74. }
  75. if (!_rx_active) continue;
  76. if (b == KISS_FESC) {
  77. _rx_escaped = true;
  78. continue;
  79. }
  80. if (_rx_escaped) {
  81. _rx_escaped = false;
  82. if (b == KISS_TFEND) b = KISS_FEND;
  83. else if (b == KISS_TFESC) b = KISS_FESC;
  84. else continue;
  85. }
  86. if (_rx_len < KISS_MAX_FRAME_SIZE) {
  87. _rx_buf[_rx_len++] = b;
  88. } else {
  89. /* Buffer full with no FEND; reset so we don't stay stuck ignoring input. */
  90. _rx_len = 0;
  91. _rx_escaped = false;
  92. _rx_active = false;
  93. }
  94. }
  95. processTx();
  96. }
  97. void KissModem::processFrame() {
  98. if (_rx_len < 1) return;
  99. uint8_t type_byte = _rx_buf[0];
  100. if (type_byte == KISS_CMD_RETURN) return;
  101. uint8_t port = (type_byte >> 4) & 0x0F;
  102. uint8_t cmd = type_byte & 0x0F;
  103. if (port != 0) return;
  104. const uint8_t* data = &_rx_buf[1];
  105. uint16_t data_len = _rx_len - 1;
  106. switch (cmd) {
  107. case KISS_CMD_DATA:
  108. if (data_len > 0 && data_len <= KISS_MAX_PACKET_SIZE && !_has_pending_tx) {
  109. memcpy(_pending_tx, data, data_len);
  110. _pending_tx_len = data_len;
  111. _has_pending_tx = true;
  112. } else if (_has_pending_tx) {
  113. writeHardwareError(HW_ERR_TX_BUSY);
  114. }
  115. break;
  116. case KISS_CMD_TXDELAY:
  117. if (data_len >= 1) _txdelay = data[0];
  118. break;
  119. case KISS_CMD_PERSISTENCE:
  120. if (data_len >= 1) _persistence = data[0];
  121. break;
  122. case KISS_CMD_SLOTTIME:
  123. if (data_len >= 1) _slottime = data[0];
  124. break;
  125. case KISS_CMD_TXTAIL:
  126. if (data_len >= 1) _txtail = data[0];
  127. break;
  128. case KISS_CMD_FULLDUPLEX:
  129. if (data_len >= 1) _fullduplex = data[0];
  130. break;
  131. case KISS_CMD_SETHARDWARE:
  132. if (data_len >= 1) {
  133. handleHardwareCommand(data[0], data + 1, data_len - 1);
  134. }
  135. break;
  136. default:
  137. break;
  138. }
  139. }
  140. void KissModem::handleHardwareCommand(uint8_t sub_cmd, const uint8_t* data, uint16_t len) {
  141. switch (sub_cmd) {
  142. case HW_CMD_GET_IDENTITY:
  143. handleGetIdentity();
  144. break;
  145. case HW_CMD_GET_RANDOM:
  146. handleGetRandom(data, len);
  147. break;
  148. case HW_CMD_VERIFY_SIGNATURE:
  149. handleVerifySignature(data, len);
  150. break;
  151. case HW_CMD_SIGN_DATA:
  152. handleSignData(data, len);
  153. break;
  154. case HW_CMD_ENCRYPT_DATA:
  155. handleEncryptData(data, len);
  156. break;
  157. case HW_CMD_DECRYPT_DATA:
  158. handleDecryptData(data, len);
  159. break;
  160. case HW_CMD_KEY_EXCHANGE:
  161. handleKeyExchange(data, len);
  162. break;
  163. case HW_CMD_HASH:
  164. handleHash(data, len);
  165. break;
  166. case HW_CMD_SET_RADIO:
  167. handleSetRadio(data, len);
  168. break;
  169. case HW_CMD_SET_TX_POWER:
  170. handleSetTxPower(data, len);
  171. break;
  172. case HW_CMD_GET_RADIO:
  173. handleGetRadio();
  174. break;
  175. case HW_CMD_GET_TX_POWER:
  176. handleGetTxPower();
  177. break;
  178. case HW_CMD_GET_VERSION:
  179. handleGetVersion();
  180. break;
  181. case HW_CMD_GET_CURRENT_RSSI:
  182. handleGetCurrentRssi();
  183. break;
  184. case HW_CMD_IS_CHANNEL_BUSY:
  185. handleIsChannelBusy();
  186. break;
  187. case HW_CMD_GET_AIRTIME:
  188. handleGetAirtime(data, len);
  189. break;
  190. case HW_CMD_GET_NOISE_FLOOR:
  191. handleGetNoiseFloor();
  192. break;
  193. case HW_CMD_GET_STATS:
  194. handleGetStats();
  195. break;
  196. case HW_CMD_GET_BATTERY:
  197. handleGetBattery();
  198. break;
  199. case HW_CMD_PING:
  200. handlePing();
  201. break;
  202. case HW_CMD_GET_SENSORS:
  203. handleGetSensors(data, len);
  204. break;
  205. case HW_CMD_GET_MCU_TEMP:
  206. handleGetMCUTemp();
  207. break;
  208. case HW_CMD_REBOOT:
  209. handleReboot();
  210. break;
  211. case HW_CMD_GET_DEVICE_NAME:
  212. handleGetDeviceName();
  213. break;
  214. case HW_CMD_SET_SIGNAL_REPORT:
  215. handleSetSignalReport(data, len);
  216. break;
  217. case HW_CMD_GET_SIGNAL_REPORT:
  218. handleGetSignalReport();
  219. break;
  220. default:
  221. writeHardwareError(HW_ERR_UNKNOWN_CMD);
  222. break;
  223. }
  224. }
  225. void KissModem::processTx() {
  226. switch (_tx_state) {
  227. case TX_IDLE:
  228. if (_has_pending_tx) {
  229. if (_fullduplex) {
  230. _tx_timer = millis();
  231. _tx_state = TX_DELAY;
  232. } else {
  233. _tx_timer = millis();
  234. _tx_state = TX_WAIT_CLEAR;
  235. }
  236. }
  237. break;
  238. case TX_WAIT_CLEAR:
  239. if (!_radio.isReceiving()) {
  240. uint8_t rand_val;
  241. _rng.random(&rand_val, 1);
  242. if (rand_val <= _persistence) {
  243. _tx_timer = millis();
  244. _tx_state = TX_DELAY;
  245. } else {
  246. _tx_timer = millis();
  247. _tx_state = TX_SLOT_WAIT;
  248. }
  249. } else if (millis() - _tx_timer >= _radio.getEstAirtimeFor(KISS_MAX_PACKET_SIZE) * KISS_TX_TIMEOUT_FACTOR) {
  250. _tx_timer = millis();
  251. _tx_state = TX_DELAY;
  252. }
  253. break;
  254. case TX_SLOT_WAIT:
  255. if (millis() - _tx_timer >= (uint32_t)_slottime * 10) {
  256. _tx_timer = millis();
  257. _tx_state = TX_WAIT_CLEAR;
  258. }
  259. break;
  260. case TX_DELAY:
  261. if (millis() - _tx_timer >= (uint32_t)_txdelay * 10) {
  262. if (_radio.startSendRaw(_pending_tx, _pending_tx_len)) {
  263. _tx_timer = millis();
  264. _tx_state = TX_SENDING;
  265. } else {
  266. uint8_t result = 0x00;
  267. writeHardwareFrame(HW_RESP_TX_DONE, &result, 1);
  268. _has_pending_tx = false;
  269. _tx_state = TX_IDLE;
  270. }
  271. }
  272. break;
  273. case TX_SENDING:
  274. if (_radio.isSendComplete()) {
  275. _radio.onSendFinished();
  276. uint8_t result = 0x01;
  277. writeHardwareFrame(HW_RESP_TX_DONE, &result, 1);
  278. _has_pending_tx = false;
  279. _tx_state = TX_IDLE;
  280. } else if (millis() - _tx_timer >= _radio.getEstAirtimeFor(_pending_tx_len) * KISS_TX_TIMEOUT_FACTOR) {
  281. _radio.onSendFinished();
  282. uint8_t result = 0x00;
  283. writeHardwareFrame(HW_RESP_TX_DONE, &result, 1);
  284. _has_pending_tx = false;
  285. _tx_state = TX_IDLE;
  286. }
  287. break;
  288. }
  289. }
  290. void KissModem::onPacketReceived(int8_t snr, int8_t rssi, const uint8_t* packet, uint16_t len) {
  291. writeFrame(KISS_CMD_DATA, packet, len);
  292. if (_signal_report_enabled) {
  293. uint8_t meta[2] = { (uint8_t)snr, (uint8_t)rssi };
  294. writeHardwareFrame(HW_RESP_RX_META, meta, 2);
  295. }
  296. }
  297. void KissModem::handleGetIdentity() {
  298. writeHardwareFrame(HW_RESP(HW_CMD_GET_IDENTITY), _identity.pub_key, PUB_KEY_SIZE);
  299. }
  300. void KissModem::handleGetRandom(const uint8_t* data, uint16_t len) {
  301. if (len < 1) {
  302. writeHardwareError(HW_ERR_INVALID_LENGTH);
  303. return;
  304. }
  305. uint8_t requested = data[0];
  306. if (requested < 1 || requested > 64) {
  307. writeHardwareError(HW_ERR_INVALID_PARAM);
  308. return;
  309. }
  310. uint8_t buf[64];
  311. _rng.random(buf, requested);
  312. writeHardwareFrame(HW_RESP(HW_CMD_GET_RANDOM), buf, requested);
  313. }
  314. void KissModem::handleVerifySignature(const uint8_t* data, uint16_t len) {
  315. if (len < PUB_KEY_SIZE + SIGNATURE_SIZE + 1) {
  316. writeHardwareError(HW_ERR_INVALID_LENGTH);
  317. return;
  318. }
  319. mesh::Identity signer(data);
  320. const uint8_t* signature = data + PUB_KEY_SIZE;
  321. const uint8_t* msg = data + PUB_KEY_SIZE + SIGNATURE_SIZE;
  322. uint16_t msg_len = len - PUB_KEY_SIZE - SIGNATURE_SIZE;
  323. uint8_t result = signer.verify(signature, msg, msg_len) ? 0x01 : 0x00;
  324. writeHardwareFrame(HW_RESP(HW_CMD_VERIFY_SIGNATURE), &result, 1);
  325. }
  326. void KissModem::handleSignData(const uint8_t* data, uint16_t len) {
  327. if (len < 1) {
  328. writeHardwareError(HW_ERR_INVALID_LENGTH);
  329. return;
  330. }
  331. uint8_t signature[SIGNATURE_SIZE];
  332. _identity.sign(signature, data, len);
  333. writeHardwareFrame(HW_RESP(HW_CMD_SIGN_DATA), signature, SIGNATURE_SIZE);
  334. }
  335. void KissModem::handleEncryptData(const uint8_t* data, uint16_t len) {
  336. if (len < PUB_KEY_SIZE + 1) {
  337. writeHardwareError(HW_ERR_INVALID_LENGTH);
  338. return;
  339. }
  340. const uint8_t* key = data;
  341. const uint8_t* plaintext = data + PUB_KEY_SIZE;
  342. uint16_t plaintext_len = len - PUB_KEY_SIZE;
  343. uint8_t buf[KISS_MAX_FRAME_SIZE];
  344. int encrypted_len = mesh::Utils::encryptThenMAC(key, buf, plaintext, plaintext_len);
  345. if (encrypted_len > 0) {
  346. writeHardwareFrame(HW_RESP(HW_CMD_ENCRYPT_DATA), buf, encrypted_len);
  347. } else {
  348. writeHardwareError(HW_ERR_ENCRYPT_FAILED);
  349. }
  350. }
  351. void KissModem::handleDecryptData(const uint8_t* data, uint16_t len) {
  352. if (len < PUB_KEY_SIZE + CIPHER_MAC_SIZE + 1) {
  353. writeHardwareError(HW_ERR_INVALID_LENGTH);
  354. return;
  355. }
  356. const uint8_t* key = data;
  357. const uint8_t* ciphertext = data + PUB_KEY_SIZE;
  358. uint16_t ciphertext_len = len - PUB_KEY_SIZE;
  359. uint8_t buf[KISS_MAX_FRAME_SIZE];
  360. int decrypted_len = mesh::Utils::MACThenDecrypt(key, buf, ciphertext, ciphertext_len);
  361. if (decrypted_len > 0) {
  362. writeHardwareFrame(HW_RESP(HW_CMD_DECRYPT_DATA), buf, decrypted_len);
  363. } else {
  364. writeHardwareError(HW_ERR_MAC_FAILED);
  365. }
  366. }
  367. void KissModem::handleKeyExchange(const uint8_t* data, uint16_t len) {
  368. if (len < PUB_KEY_SIZE) {
  369. writeHardwareError(HW_ERR_INVALID_LENGTH);
  370. return;
  371. }
  372. uint8_t shared_secret[PUB_KEY_SIZE];
  373. _identity.calcSharedSecret(shared_secret, data);
  374. writeHardwareFrame(HW_RESP(HW_CMD_KEY_EXCHANGE), shared_secret, PUB_KEY_SIZE);
  375. }
  376. void KissModem::handleHash(const uint8_t* data, uint16_t len) {
  377. if (len < 1) {
  378. writeHardwareError(HW_ERR_INVALID_LENGTH);
  379. return;
  380. }
  381. uint8_t hash[32];
  382. mesh::Utils::sha256(hash, 32, data, len);
  383. writeHardwareFrame(HW_RESP(HW_CMD_HASH), hash, 32);
  384. }
  385. void KissModem::handleSetRadio(const uint8_t* data, uint16_t len) {
  386. if (len < 10) {
  387. writeHardwareError(HW_ERR_INVALID_LENGTH);
  388. return;
  389. }
  390. if (!_setRadioCallback) {
  391. writeHardwareError(HW_ERR_NO_CALLBACK);
  392. return;
  393. }
  394. memcpy(&_config.freq_hz, data, 4);
  395. memcpy(&_config.bw_hz, data + 4, 4);
  396. _config.sf = data[8];
  397. _config.cr = data[9];
  398. _setRadioCallback(_config.freq_hz / 1000000.0f, _config.bw_hz / 1000.0f, _config.sf, _config.cr);
  399. writeHardwareFrame(HW_RESP_OK, nullptr, 0);
  400. }
  401. void KissModem::handleSetTxPower(const uint8_t* data, uint16_t len) {
  402. if (len < 1) {
  403. writeHardwareError(HW_ERR_INVALID_LENGTH);
  404. return;
  405. }
  406. if (!_setTxPowerCallback) {
  407. writeHardwareError(HW_ERR_NO_CALLBACK);
  408. return;
  409. }
  410. _config.tx_power = data[0];
  411. _setTxPowerCallback(data[0]);
  412. writeHardwareFrame(HW_RESP_OK, nullptr, 0);
  413. }
  414. void KissModem::handleGetRadio() {
  415. uint8_t buf[10];
  416. memcpy(buf, &_config.freq_hz, 4);
  417. memcpy(buf + 4, &_config.bw_hz, 4);
  418. buf[8] = _config.sf;
  419. buf[9] = _config.cr;
  420. writeHardwareFrame(HW_RESP(HW_CMD_GET_RADIO), buf, 10);
  421. }
  422. void KissModem::handleGetTxPower() {
  423. writeHardwareFrame(HW_RESP(HW_CMD_GET_TX_POWER), &_config.tx_power, 1);
  424. }
  425. void KissModem::handleGetVersion() {
  426. uint8_t buf[2];
  427. buf[0] = KISS_FIRMWARE_VERSION;
  428. buf[1] = 0;
  429. writeHardwareFrame(HW_RESP(HW_CMD_GET_VERSION), buf, 2);
  430. }
  431. void KissModem::handleGetCurrentRssi() {
  432. if (!_getCurrentRssiCallback) {
  433. writeHardwareError(HW_ERR_NO_CALLBACK);
  434. return;
  435. }
  436. float rssi = _getCurrentRssiCallback();
  437. int8_t rssi_byte = (int8_t)rssi;
  438. writeHardwareFrame(HW_RESP(HW_CMD_GET_CURRENT_RSSI), (uint8_t*)&rssi_byte, 1);
  439. }
  440. void KissModem::handleIsChannelBusy() {
  441. uint8_t busy = _radio.isReceiving() ? 0x01 : 0x00;
  442. writeHardwareFrame(HW_RESP(HW_CMD_IS_CHANNEL_BUSY), &busy, 1);
  443. }
  444. void KissModem::handleGetAirtime(const uint8_t* data, uint16_t len) {
  445. if (len < 1) {
  446. writeHardwareError(HW_ERR_INVALID_LENGTH);
  447. return;
  448. }
  449. uint8_t packet_len = data[0];
  450. uint32_t airtime = _radio.getEstAirtimeFor(packet_len);
  451. writeHardwareFrame(HW_RESP(HW_CMD_GET_AIRTIME), (uint8_t*)&airtime, 4);
  452. }
  453. void KissModem::handleGetNoiseFloor() {
  454. int16_t noise_floor = _radio.getNoiseFloor();
  455. writeHardwareFrame(HW_RESP(HW_CMD_GET_NOISE_FLOOR), (uint8_t*)&noise_floor, 2);
  456. }
  457. void KissModem::handleGetStats() {
  458. if (!_getStatsCallback) {
  459. writeHardwareError(HW_ERR_NO_CALLBACK);
  460. return;
  461. }
  462. uint32_t rx, tx, errors;
  463. _getStatsCallback(&rx, &tx, &errors);
  464. uint8_t buf[12];
  465. memcpy(buf, &rx, 4);
  466. memcpy(buf + 4, &tx, 4);
  467. memcpy(buf + 8, &errors, 4);
  468. writeHardwareFrame(HW_RESP(HW_CMD_GET_STATS), buf, 12);
  469. }
  470. void KissModem::handleGetBattery() {
  471. uint16_t mv = _board.getBattMilliVolts();
  472. writeHardwareFrame(HW_RESP(HW_CMD_GET_BATTERY), (uint8_t*)&mv, 2);
  473. }
  474. void KissModem::handlePing() {
  475. writeHardwareFrame(HW_RESP(HW_CMD_PING), nullptr, 0);
  476. }
  477. void KissModem::handleGetSensors(const uint8_t* data, uint16_t len) {
  478. if (len < 1) {
  479. writeHardwareError(HW_ERR_INVALID_LENGTH);
  480. return;
  481. }
  482. uint8_t permissions = data[0];
  483. CayenneLPP telemetry(255);
  484. if (_sensors.querySensors(permissions, telemetry)) {
  485. writeHardwareFrame(HW_RESP(HW_CMD_GET_SENSORS), telemetry.getBuffer(), telemetry.getSize());
  486. } else {
  487. writeHardwareFrame(HW_RESP(HW_CMD_GET_SENSORS), nullptr, 0);
  488. }
  489. }
  490. void KissModem::handleGetMCUTemp() {
  491. float temp = _board.getMCUTemperature();
  492. if (isnan(temp)) {
  493. writeHardwareError(HW_ERR_NO_CALLBACK);
  494. return;
  495. }
  496. int16_t temp_tenths = (int16_t)(temp * 10.0f);
  497. writeHardwareFrame(HW_RESP(HW_CMD_GET_MCU_TEMP), (uint8_t*)&temp_tenths, 2);
  498. }
  499. void KissModem::handleReboot() {
  500. writeHardwareFrame(HW_RESP_OK, nullptr, 0);
  501. _serial.flush();
  502. delay(50);
  503. _board.reboot();
  504. }
  505. void KissModem::handleGetDeviceName() {
  506. const char* name = _board.getManufacturerName();
  507. writeHardwareFrame(HW_RESP(HW_CMD_GET_DEVICE_NAME), (const uint8_t*)name, strlen(name));
  508. }
  509. void KissModem::handleSetSignalReport(const uint8_t* data, uint16_t len) {
  510. if (len < 1) {
  511. writeHardwareError(HW_ERR_INVALID_LENGTH);
  512. return;
  513. }
  514. _signal_report_enabled = (data[0] != 0x00);
  515. uint8_t val = _signal_report_enabled ? 0x01 : 0x00;
  516. writeHardwareFrame(HW_RESP(HW_CMD_GET_SIGNAL_REPORT), &val, 1);
  517. }
  518. void KissModem::handleGetSignalReport() {
  519. uint8_t val = _signal_report_enabled ? 0x01 : 0x00;
  520. writeHardwareFrame(HW_RESP(HW_CMD_GET_SIGNAL_REPORT), &val, 1);
  521. }