shellmatta_ymodem.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. /**
  2. * @file shellmatta_ymodem.c
  3. * @brief ymodem functions of shellmatta
  4. * @author Simon Fischer <dev@s-fischer.net>
  5. */
  6. #include "shellmatta_ymodem.h"
  7. #include "shellmatta_crc.h"
  8. #include "shellmatta_utils.h"
  9. #include <stddef.h>
  10. /* current ymodem packet size */
  11. uint16_t yModemPacketSize = YMODEM_PACKET_SIZE;
  12. /* packet counter - counts all correctly received packets during one transmission */
  13. uint16_t shellmatta_ymodem_packet_counter = 0u;
  14. /* counts total amount of data bytes received during one transmission */
  15. uint32_t shellmatta_ymodem_total_bytes_received = 0u;
  16. /* counts total amount of data bytes received during one packet */
  17. uint16_t shellmatta_ymodem_byte_counter = 0u;
  18. uint8_t fileNameDelimiterPosition = 255u;
  19. uint8_t fileSizeDelimiterPosition = 255u;
  20. /* structure of a packet consisting of data and metadata */
  21. shellmatta_ymodem_packet_t shellmatta_ymodem_packet;
  22. /* holds information about which data type is currently actice: YMODEM_NONE, YMODEM_HEADER, YMODEM_BODY, YMODEM_FOOTER */
  23. shellmatta_ymodem_datatype_t shellmatta_ymodem_current_data_type = YMODEM_NONE;
  24. /* structure of ymodem callback functions */
  25. shellmatta_ymodem_callbacks_t shellmatta_ymodem_callbacks;
  26. /* flag to disable byte processing in ymodem */
  27. bool ymodem_is_enabled = true;
  28. /**
  29. * @brief Initialise the ymodem prior to actually receiving data
  30. * @param[in, out] handle shellmatta handle of the instance
  31. * @param[in] recvBuffer pointer to the buffer to save the received payload in
  32. * @param[in] fileSize pointer to the file size variable
  33. * @param[in] packetSize pointer to the packet size variable
  34. * @param[in] callbacks callback functions for the ymodem module
  35. */
  36. void shellmatta_ymodem_init(shellmatta_handle_t handle,
  37. uint8_t* recvBuffer,
  38. uint32_t* fileSize,
  39. uint16_t* packetSize,
  40. shellmatta_ymodem_callbacks_t callbacks)
  41. {
  42. if ((void*)0u == recvBuffer) return;
  43. /* init packet structure */
  44. shellmatta_ymodem_packet.packetNumber = 0u;
  45. shellmatta_ymodem_packet.reversePacketNumber = 0u;
  46. shellmatta_ymodem_packet.packetCrc = 0u;
  47. shellmatta_ymodem_packet.packetSize = packetSize;
  48. shellmatta_ymodem_packet.fileSize = fileSize;
  49. shellmatta_ymodem_packet.packetData = recvBuffer;
  50. /* init callbacks */
  51. shellmatta_ymodem_callbacks = callbacks;
  52. /* init ymodem state */
  53. shellmatta_ymodem_set_state(handle, WAIT_FOR_START);
  54. /* init current data type */
  55. shellmatta_ymodem_current_data_type = YMODEM_NONE;
  56. }
  57. /**
  58. * @brief change the current state of the ymodem module
  59. * @param[in, out] handle shellmatta handle of the instance
  60. * @param[in] newState the state the ymodem module should be changed to
  61. */
  62. void shellmatta_ymodem_set_state(shellmatta_handle_t handle, shellmatta_ymodem_state_t newState)
  63. {
  64. ((shellmatta_instance_t*)handle)->ymodemState = newState;
  65. }
  66. /**
  67. * @brief Returns the current state of the ymodem module
  68. * @param[in, out] handle shellmatta handle of the instance
  69. * @return the current state of the ymodem module
  70. */
  71. shellmatta_ymodem_state_t shellmatta_ymodem_get_state(shellmatta_handle_t handle)
  72. {
  73. return ((shellmatta_instance_t*)handle)->ymodemState;
  74. }
  75. /**
  76. * @brief State machine that processes bytewise input during active ymodem transmission
  77. * @param[in, out] handle shellmatta handle of the instance
  78. * @param[in] byteIn the byte to be processed
  79. * @note won't do anything if ymodem module is not initialized
  80. */
  81. void shellmatta_ymodem_receive_packet(shellmatta_handle_t handle, uint8_t byteIn)
  82. {
  83. static uint32_t fileSize = 0u;
  84. static char fileSizeStr[7u]; /* hopefully no more bytes than a million will ever be transmitted */
  85. shellmatta_ymodem_rcv_retcode_t recvRetCode;
  86. /* skip byte processing if ymodem is not enabled */
  87. if (!ymodem_is_enabled)
  88. {
  89. return;
  90. }
  91. recvRetCode = shellmatta_ymodem_receive_byte(handle, byteIn);
  92. switch (shellmatta_ymodem_get_state(handle))
  93. {
  94. case INACTIVE:
  95. /* cant do anything if ymodem module was not correctly initialized */
  96. return;
  97. case WAIT_FOR_START:
  98. /* wait for start symbol of a packet */
  99. switch (shellmatta_ymodem_current_data_type)
  100. {
  101. case YMODEM_NONE: /* go here if the header packet is to be expected */
  102. if (recvRetCode == SOH_RECEIVED || recvRetCode == STX_RECEIVED)
  103. {
  104. /* reset packet counter */
  105. shellmatta_ymodem_packet_counter = 0u;
  106. shellmatta_ymodem_byte_counter = 0u;
  107. shellmatta_ymodem_set_state(handle, RECEIVE_PACKET);
  108. shellmatta_ymodem_current_data_type = YMODEM_HEADER;
  109. }
  110. break;
  111. case YMODEM_HEADER: /* go here if the first body packet is to be expected */
  112. if (recvRetCode == STX_RECEIVED || SOH_RECEIVED)
  113. {
  114. shellmatta_ymodem_byte_counter = 0u;
  115. shellmatta_ymodem_set_state(handle, RECEIVE_PACKET);
  116. shellmatta_ymodem_current_data_type = YMODEM_BODY;
  117. }
  118. break;
  119. case YMODEM_BODY: /* go here if the data transmission loop is active */
  120. if (recvRetCode == SOH_RECEIVED || recvRetCode == STX_RECEIVED)
  121. {
  122. shellmatta_ymodem_byte_counter = 0u;
  123. /* stay in body when SOH was received */
  124. shellmatta_ymodem_set_state(handle, RECEIVE_PACKET);
  125. shellmatta_ymodem_current_data_type = YMODEM_BODY;
  126. }
  127. else /* go here if the end of transmission symbol is received */
  128. if (recvRetCode == EOT_RECEIVED)
  129. {
  130. shellmatta_ymodem_byte_counter = 0u;
  131. /* answer with ACK */
  132. shellmatta_ymodem_ack(handle);
  133. /* then send 0x43 symbol */
  134. shellmatta_ymodem_control(handle, YMODEM_CRC);
  135. /* go to footer when EOT was received */
  136. shellmatta_ymodem_current_data_type = YMODEM_FOOTER;
  137. /* then wait for SOH */
  138. }
  139. break;
  140. case YMODEM_FOOTER: /* go here if the end of transmission packet is to be expected */
  141. /* it _may_ be, that EOT is sent multiple times, repeat previous procedure */
  142. if (recvRetCode == EOT_RECEIVED)
  143. {
  144. /* answer with ACK */
  145. shellmatta_ymodem_ack(handle);
  146. /* then send 0x43 symbol */
  147. shellmatta_ymodem_control(handle, YMODEM_CRC);
  148. }
  149. if (recvRetCode == SOH_RECEIVED)
  150. {
  151. /* reset packet counter */
  152. shellmatta_ymodem_packet_counter = 0u;
  153. /* reset byte counter to avoid error by one caused by previously received EOT symbol */
  154. shellmatta_ymodem_byte_counter = 0u;
  155. shellmatta_ymodem_set_state(handle, RECEIVE_PACKET);
  156. }
  157. break;
  158. default:
  159. break;
  160. }
  161. break;
  162. case RECEIVE_PACKET:
  163. /* receiving data */
  164. if (recvRetCode == DATA_RECEIVED)
  165. {
  166. switch (shellmatta_ymodem_current_data_type)
  167. {
  168. case YMODEM_HEADER:
  169. switch (shellmatta_ymodem_byte_counter)
  170. {
  171. /* first two bytes are packet number and reverse packet number */
  172. case 0:
  173. shellmatta_ymodem_packet.packetNumber = byteIn;
  174. break;
  175. case 1:
  176. shellmatta_ymodem_packet.reversePacketNumber = byteIn;
  177. break;
  178. default:
  179. break;
  180. }
  181. /* after packet numbers, data field begins */
  182. if ((shellmatta_ymodem_byte_counter >= YMODEM_HEADER_DATA_OFFSET) &&
  183. (shellmatta_ymodem_byte_counter < YMODEM_HEADER_CRC_POSITION))
  184. {
  185. shellmatta_ymodem_packet.packetData[shellmatta_ymodem_byte_counter - YMODEM_HEADER_DATA_OFFSET] = byteIn;
  186. /* check for filename */
  187. if (shellmatta_ymodem_byte_counter < fileNameDelimiterPosition)
  188. {
  189. /* find NULL-Terminator in filename */
  190. if (byteIn == YMODEM_NULL)
  191. {
  192. fileNameDelimiterPosition = shellmatta_ymodem_byte_counter + 1;
  193. }
  194. }
  195. /* after filename the filesize begins */
  196. else
  197. if (shellmatta_ymodem_byte_counter >= fileNameDelimiterPosition &&
  198. (shellmatta_ymodem_byte_counter < fileSizeDelimiterPosition))
  199. {
  200. /* find space as delimiter after filesize */
  201. if (byteIn == YMODEM_SPACE)
  202. {
  203. fileSizeDelimiterPosition = shellmatta_ymodem_byte_counter;
  204. /* convert file size string to actual number */
  205. fileSize = utils_shellAsciiToUInt32(fileSizeStr, fileSizeDelimiterPosition - fileNameDelimiterPosition);
  206. *shellmatta_ymodem_packet.fileSize = fileSize;
  207. }
  208. /* save characters in string otherwise */
  209. else
  210. {
  211. fileSizeStr[shellmatta_ymodem_byte_counter - fileNameDelimiterPosition] = byteIn;
  212. }
  213. }
  214. else
  215. {
  216. /* some meta data from sender, possibly file date */
  217. /* not needed for now */
  218. }
  219. }
  220. /* after data field, crc begins */
  221. if (shellmatta_ymodem_byte_counter >= YMODEM_HEADER_CRC_POSITION)
  222. {
  223. if (shellmatta_ymodem_byte_counter == YMODEM_HEADER_CRC_POSITION)
  224. {
  225. /* upper byte */
  226. shellmatta_ymodem_packet.packetCrc = byteIn << 8u;
  227. }
  228. if (shellmatta_ymodem_byte_counter == YMODEM_HEADER_CRC_POSITION + 1)
  229. {
  230. /* lower byte */
  231. shellmatta_ymodem_packet.packetCrc |= byteIn;
  232. /* change ymodem state to check packet */
  233. shellmatta_ymodem_check_packet(handle);
  234. }
  235. }
  236. shellmatta_ymodem_byte_counter++;
  237. break;
  238. case YMODEM_BODY:
  239. switch (shellmatta_ymodem_byte_counter)
  240. {
  241. case 0:
  242. shellmatta_ymodem_packet.packetNumber = byteIn;
  243. break;
  244. case 1:
  245. shellmatta_ymodem_packet.reversePacketNumber = byteIn;
  246. break;
  247. default:
  248. break;
  249. }
  250. /* after data field, crc begins */
  251. if (shellmatta_ymodem_byte_counter >= yModemPacketSize + YMODEM_HEADER_DATA_OFFSET)
  252. {
  253. if (shellmatta_ymodem_byte_counter == yModemPacketSize + YMODEM_HEADER_DATA_OFFSET)
  254. {
  255. /* upper byte */
  256. shellmatta_ymodem_packet.packetCrc = byteIn << 8u;
  257. }
  258. if (shellmatta_ymodem_byte_counter == yModemPacketSize + YMODEM_HEADER_DATA_OFFSET + 1)
  259. {
  260. /* lower byte */
  261. shellmatta_ymodem_packet.packetCrc |= byteIn;
  262. /* change ymodem state to check packet */
  263. shellmatta_ymodem_check_packet(handle);
  264. }
  265. }
  266. else
  267. if ((shellmatta_ymodem_byte_counter > 1) &&
  268. (shellmatta_ymodem_byte_counter < (yModemPacketSize + YMODEM_HEADER_DATA_OFFSET)))
  269. {
  270. shellmatta_ymodem_packet.packetData[shellmatta_ymodem_byte_counter - YMODEM_HEADER_DATA_OFFSET] = byteIn;
  271. }
  272. shellmatta_ymodem_byte_counter++;
  273. break;
  274. case YMODEM_FOOTER:
  275. /* reset packet counter */
  276. shellmatta_ymodem_packet_counter = 0;
  277. switch (shellmatta_ymodem_byte_counter)
  278. {
  279. case 0:
  280. shellmatta_ymodem_packet.packetNumber = byteIn;
  281. break;
  282. case 1:
  283. shellmatta_ymodem_packet.reversePacketNumber = byteIn;
  284. break;
  285. default:
  286. break;
  287. }
  288. /* after data field, crc begins */
  289. if (shellmatta_ymodem_byte_counter >= yModemPacketSize + YMODEM_HEADER_DATA_OFFSET)
  290. {
  291. if (shellmatta_ymodem_byte_counter == yModemPacketSize + YMODEM_HEADER_DATA_OFFSET)
  292. {
  293. /* upper byte */
  294. shellmatta_ymodem_packet.packetCrc = byteIn << 8u;
  295. }
  296. if (shellmatta_ymodem_byte_counter == yModemPacketSize + YMODEM_HEADER_DATA_OFFSET + 1)
  297. {
  298. /* lower byte */
  299. shellmatta_ymodem_packet.packetCrc |= byteIn;
  300. /* change ymodem state to check packet */
  301. shellmatta_ymodem_check_packet(handle);
  302. }
  303. }
  304. else
  305. if ((shellmatta_ymodem_byte_counter > 1) &&
  306. (shellmatta_ymodem_byte_counter < (yModemPacketSize + YMODEM_HEADER_DATA_OFFSET)))
  307. {
  308. /* save data */
  309. shellmatta_ymodem_packet.packetData[shellmatta_ymodem_byte_counter - YMODEM_HEADER_DATA_OFFSET] = byteIn;
  310. }
  311. shellmatta_ymodem_byte_counter++;
  312. break;
  313. default:
  314. break;
  315. }
  316. }
  317. break;
  318. default:
  319. break;
  320. }
  321. }
  322. /**
  323. * @brief Gives a return code depending on symbol received at different states of the ymodem module
  324. * @param[in, out] handle shellmatta handle of the instance
  325. * @param[in] byteIn currently processed input byte
  326. */
  327. shellmatta_ymodem_rcv_retcode_t shellmatta_ymodem_receive_byte(shellmatta_handle_t handle, uint8_t byteIn)
  328. {
  329. shellmatta_ymodem_rcv_retcode_t ret;
  330. if (WAIT_FOR_START == shellmatta_ymodem_get_state(handle))
  331. {
  332. switch (byteIn)
  333. {
  334. case YMODEM_SOH:
  335. /* start of header -> default packet size */
  336. yModemPacketSize = YMODEM_PACKET_SIZE;
  337. ret = SOH_RECEIVED;
  338. break;
  339. case YMODEM_STX:
  340. /* start of header -> extended packet size */
  341. yModemPacketSize = YMODEM_PACKET_SIZE_1K;
  342. ret = STX_RECEIVED;
  343. break;
  344. case YMODEM_EOT:
  345. /* end of transmission -> data transmission complete */
  346. ret = EOT_RECEIVED;
  347. break;
  348. case YMODEM_CA:
  349. /* cancel transmssion -> reset state machine */
  350. ret = CA_RECEIVED;
  351. break;
  352. default:
  353. /* no other data should be received in this state */
  354. ret = ERROR;
  355. break;
  356. }
  357. /* give packet size back to upper layer */
  358. *shellmatta_ymodem_packet.packetSize = yModemPacketSize;
  359. }
  360. else
  361. if (RECEIVE_PACKET == shellmatta_ymodem_get_state(handle))
  362. {
  363. ret = DATA_RECEIVED;
  364. }
  365. return ret;
  366. }
  367. /**
  368. * @brief Checks packet data for validity and sends ACK or NAK accordingly
  369. * @param[in, out] handle shellmatta handle of the instance
  370. * @note Only to be called after full packet is received
  371. * @note Will call yModemRecvPacketCallback when packet is received correctly
  372. * @note Will call ymodemTransmissionCompleteCallback when transmission is completed
  373. */
  374. void shellmatta_ymodem_check_packet(shellmatta_handle_t handle)
  375. {
  376. uint16_t computedCrc = 0u;
  377. if (shellmatta_ymodem_packet.packetNumber != (0xFF - shellmatta_ymodem_packet.reversePacketNumber))
  378. {
  379. shellmatta_ymodem_nak(handle);
  380. return;
  381. }
  382. /* compare to internal packet counter */
  383. if (shellmatta_ymodem_packet.packetNumber != ((uint8_t)shellmatta_ymodem_packet_counter)) /* uint8_t cast to cause overflow */
  384. {
  385. shellmatta_ymodem_nak(handle);
  386. return;
  387. }
  388. /* compare packet crc only after other checks succeeded */
  389. computedCrc = crc16Calc((const char*)shellmatta_ymodem_packet.packetData, yModemPacketSize);
  390. //! uncomment to ignore crc check for debug purposes
  391. if (shellmatta_ymodem_packet.packetCrc != computedCrc)
  392. {
  393. shellmatta_ymodem_nak(handle);
  394. return;
  395. }
  396. /* if previous checks passed, packet is acknowledged */
  397. (void) computedCrc;
  398. shellmatta_ymodem_total_bytes_received += shellmatta_ymodem_byte_counter;
  399. shellmatta_ymodem_byte_counter = 0u;
  400. shellmatta_ymodem_ack(handle);
  401. if (shellmatta_ymodem_current_data_type == YMODEM_HEADER)
  402. {
  403. /* send additional CRC symbol if the packet was a header packet */
  404. shellmatta_ymodem_control(handle, YMODEM_CRC);
  405. }
  406. /* callback handling */
  407. switch (shellmatta_ymodem_current_data_type)
  408. {
  409. case YMODEM_NONE:
  410. /* shouldn't happen */
  411. break;
  412. case YMODEM_HEADER:
  413. /* nothing to do after receiving the header packet */
  414. break;
  415. case YMODEM_BODY:
  416. /* call the ymodem receive packet callback */
  417. if (NULL != shellmatta_ymodem_callbacks.yModemRecvPacketCallback)
  418. {
  419. shellmatta_ymodem_callbacks.yModemRecvPacketCallback();
  420. }
  421. break;
  422. case YMODEM_FOOTER:
  423. /* call the ymodem transmission complete callback */
  424. if (NULL != shellmatta_ymodem_callbacks.ymodemTransmissionCompleteCallback)
  425. {
  426. shellmatta_ymodem_callbacks.ymodemTransmissionCompleteCallback();
  427. }
  428. break;
  429. default:
  430. break;
  431. }
  432. }
  433. /**
  434. * @brief Acknowledge handling. Will send ACK and set ymodem state back to WAIT_FOR_START
  435. * @param[in, out] handle shellmatta handle of the instance
  436. */
  437. void shellmatta_ymodem_ack(shellmatta_handle_t handle)
  438. {
  439. shellmatta_ymodem_control(handle, YMODEM_ACK);
  440. shellmatta_ymodem_packet_counter++;
  441. shellmatta_ymodem_set_state(handle, WAIT_FOR_START);
  442. }
  443. /**
  444. * @brief Not-Acknowledge handling. Will send NAK and set ymodem state back to WAIT_FOR_START
  445. * @param[in, out] handle shellmatta handle of the instance
  446. */
  447. void shellmatta_ymodem_nak(shellmatta_handle_t handle)
  448. {
  449. shellmatta_ymodem_set_state(handle, WAIT_FOR_START);
  450. /* set back current data type to prevent false forward stepping */
  451. switch (shellmatta_ymodem_current_data_type)
  452. {
  453. case YMODEM_NONE:
  454. /* no handling needed */
  455. break;
  456. case YMODEM_HEADER:
  457. shellmatta_ymodem_control(handle, YMODEM_NAK);
  458. break;
  459. case YMODEM_BODY:
  460. /* YMODEM_BODY stays in YMODEM_BODY */
  461. shellmatta_ymodem_control(handle, YMODEM_NAK);
  462. shellmatta_ymodem_current_data_type = YMODEM_BODY;
  463. break;
  464. case YMODEM_FOOTER:
  465. /* YMODEM_FOOTER as well */
  466. shellmatta_ymodem_current_data_type = YMODEM_FOOTER;
  467. break;
  468. default:
  469. break;
  470. }
  471. }
  472. /**
  473. * @brief forwards the given character to write-function without formatting
  474. * @param[in] handle shellmatta handle of the instance
  475. * @param[in] c character to be sent
  476. */
  477. void shellmatta_ymodem_control(shellmatta_handle_t handle, const char c)
  478. {
  479. ((shellmatta_instance_t*)handle)->write(&c, 1);
  480. }
  481. /**
  482. * @brief reset function for the ymodem module
  483. * @param[in, out] handle shellmatta handle of the instance
  484. * @param[in] doCancel flag to execute the cancel-callback
  485. */
  486. void shellmatta_ymodem_reset(shellmatta_handle_t handle, bool doCancel)
  487. {
  488. /* call cancel callback function */
  489. if (doCancel)
  490. {
  491. /* send cancel symbol */
  492. shellmatta_ymodem_control(handle, YMODEM_CA);
  493. if (NULL != shellmatta_ymodem_callbacks.yModemCancelCallback)
  494. {
  495. shellmatta_ymodem_callbacks.yModemCancelCallback();
  496. }
  497. }
  498. /* reset global variables */
  499. shellmatta_ymodem_byte_counter = 0u;
  500. shellmatta_ymodem_packet_counter = 0u;
  501. shellmatta_ymodem_total_bytes_received = 0u;
  502. fileNameDelimiterPosition = 255u;
  503. fileSizeDelimiterPosition = 255u;
  504. /* reset all ymodem_packet data */
  505. shellmatta_ymodem_packet.packetNumber = 0u;
  506. shellmatta_ymodem_packet.reversePacketNumber = 0u;
  507. shellmatta_ymodem_packet.packetCrc = 0u;
  508. shellmatta_ymodem_packet.packetSize = 0u;
  509. shellmatta_ymodem_packet.fileSize = 0u;
  510. shellmatta_ymodem_packet.packetData = 0u;
  511. /* set ymodem to inactive */
  512. shellmatta_ymodem_set_state(handle, INACTIVE);
  513. shellmatta_ymodem_current_data_type = YMODEM_NONE;
  514. #ifdef SHELLMATTA_TRANSPORT
  515. /* re-enable transport layer */
  516. ((shellmatta_instance_t*)handle)->transportEnabled = true;
  517. #endif
  518. }
  519. shellmatta_ymodem_datatype_t shellmatta_ymodem_get_current_datatype(void)
  520. {
  521. return shellmatta_ymodem_current_data_type;
  522. }
  523. void shellmatta_ymodem_enable(void)
  524. {
  525. ymodem_is_enabled = true;
  526. }
  527. void shellmatta_ymodem_disable(void)
  528. {
  529. ymodem_is_enabled = false;
  530. }