text: /*
   * This file is part of the MicroPython ESP32 project, https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo
   *
   * The MIT License (MIT)
   *
   * Copyright (c) 2018 LoBo (https://github.com/loboris)
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   * THE SOFTWARE.
   */
  
  /*
   * MicroPython-ESP32 YModem driver/Module
   *
   * Copyright (C) 2017 Boris Lovosevic (https://github.com/loboris)
   *
   */
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  
  #include "py/runtime.h"
  #include "py/mperrno.h"
  #include "sdkconfig.h"
  
  #if CONFIG_MICROPY_RX_BUFFER_SIZE > 1079
  
  #include "freertos/FreeRTOS.h"
  #include "freertos/task.h"
  #include "freertos/semphr.h"
  #include "rom/crc.h"
  #include "uart.h"
  #include "modymodem.h"
  #include "py/ringbuf.h"
  #include "mphalport.h"
  #include "rom/uart.h"
  
  #include <fcntl.h>
  #include "extmod/vfs_native.h"
  
  #ifdef CONFIG_MICROPY_USE_TELNET
  #include "telnet.h"
  #endif
  
  //------------------------------------------------------------------------
  static unsigned short crc16(const unsigned char *buf, unsigned long count)
  {
    unsigned short crc = 0;
    int i;
  
    while(count--) {
      crc = crc ^ *buf++ << 8;
  
      for (i=0; i<8; i++) {
        if (crc & 0x8000) crc = crc << 1 ^ 0x1021;
        else crc = crc << 1;
      }
    }
    return crc;
  }
  
  /*
  //---------------------------------------------------------------------------
  static int32_t receive_Bytes (unsigned char *buf, int size, uint32_t timeout)
  {
  	unsigned char ch;
      int cb = -1;
      int recv = 0;
  
      while (recv < size) {
      	cb = mp_hal_stdin_rx_chr(timeout);
      	if (cb < 0) break;
      	buf[recv++] = (uint8_t)cb;
      }
  	if (recv == 0) return -1;
  	return 0;
  }
  */
  
  //--------------------------------------------------------------
  static int32_t Receive_Byte (unsigned char *c, uint32_t timeout)
  {
  	int cb = mp_hal_stdin_rx_chr(timeout);
  
  	if (cb < 0) return -1;
  	*c = (uint8_t)cb;
      return 0;
  }
  
  //------------------------
  static void uart_consume()
  {
  	xSemaphoreTake(uart0_mutex, UART_SEMAPHORE_WAIT);
  	uart0_raw_input = 1;
  	xSemaphoreGive(uart0_mutex);
  	int cb = mp_hal_stdin_rx_chr(1);
      while (cb >= 0) {
      	cb = mp_hal_stdin_rx_chr(1);
      }
  	xSemaphoreTake(uart0_mutex, UART_SEMAPHORE_WAIT);
      uart0_raw_input = 0;
  	xSemaphoreGive(uart0_mutex);
  }
  
  //----------------------------------------
  static void send_Bytes(char *buf, int len)
  {
      while (len--) {
          uart_tx_one_char(*buf++);
      }
  }
  //--------------------------------
  static uint32_t Send_Byte (char c)
  {
  	send_Bytes(&c,1);
  	return 0;
  }
  
  //----------------------------
  static void send_CA ( void ) {
    Send_Byte(CA);
    Send_Byte(CA);
  }
  
  //-----------------------------
  static void send_ACK ( void ) {
    Send_Byte(ACK);
  }
  
  //----------------------------------
  static void send_ACKCRC16 ( void ) {
    Send_Byte(ACK);
    Send_Byte(CRC16);
  }
  
  //-----------------------------
  static void send_NAK ( void ) {
    Send_Byte(NAK);
  }
  
  //-------------------------------
  static void send_CRC16 ( void ) {
    Send_Byte(CRC16);
  }
  
  
  /**
    * @brief  Receive a packet from sender
    * @param  data
    * @param  timeout
    * @param  length
    *    >0: packet length
    *     0: end of transmission
    *    -1: abort by sender
    *    -2: error or crc error
    * @retval 0: normally return
    *        -1: timeout
    *        -2: abort by user
    */
  //--------------------------------------------------------------------------
  static int32_t Receive_Packet (uint8_t *data, int *length, uint32_t timeout)
  {
    int count, packet_size, i;
    unsigned char ch;
    *length = 0;
    
    // receive 1st byte
    if (Receive_Byte(&ch, timeout) < 0) {
  	  return -1;
    }
  
    switch (ch) {
      case SOH:
  		packet_size = PACKET_SIZE;
  		break;
      case STX:
  		packet_size = PACKET_1K_SIZE;
  		break;
      case EOT:
          *length = 0;
          return 0;
      case CA:
      	if (Receive_Byte(&ch, timeout) < 0) {
      		return -2;
      	}
      	if (ch == CA) {
      		*length = -1;
      		return 0;
      	}
      	else return -1;
      case ABORT1:
      case ABORT2:
      	return -2;
      default:
      	vTaskDelay(100 / portTICK_RATE_MS);
      	uart_consume();
      	return -1;
    }
  
    *data = (uint8_t)ch;
    uint8_t *dptr = data+1;
    count = packet_size + PACKET_OVERHEAD-1;
  
    for (i=0; i<count; i++) {
  	  if (Receive_Byte(&ch, timeout) < 0) {
  		  return -1;
  	  }
  	  *dptr++ = (uint8_t)ch;;
    }
  
    if (data[PACKET_SEQNO_INDEX] != ((data[PACKET_SEQNO_COMP_INDEX] ^ 0xff) & 0xff)) {
        *length = -2;
        return 0;
    }
    if (crc16(&data[PACKET_HEADER], packet_size + PACKET_TRAILER) != 0) {
        *length = -2;
        return 0;
    }
  
    *length = packet_size;
    return 0;
  }
  
  // Receive a file using the ymodem protocol.
  //-------------------------------------------------------------------------------
  int Ymodem_Receive (FILE *ffd, unsigned int maxsize, char* getname, char *errmsg)
  {
    uint8_t packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD];
    uint8_t *file_ptr;
    char file_size[128];
    unsigned int i, file_len, write_len, session_done, file_done, packets_received, errors, size = 0;
    int packet_length = 0;
    file_len = 0;
    int eof_cnt = 0;
    
    for (session_done = 0, errors = 0; ;) {
      for (packets_received = 0, file_done = 0; ;) {
        switch (Receive_Packet(packet_data, &packet_length, NAK_TIMEOUT)) {
          case 0:  // normal return
            switch (packet_length) {
              case -1:
                  // Abort by sender
                  send_ACK();
                  size = -1;
                  sprintf(errmsg, "Abort by sender");
                  goto exit;
              case -2:
                  // error
                  errors ++;
                  if (errors > 5) {
                    send_CA();
                    size = -2;
                    sprintf(errmsg, "Error limit exceeded");
                    goto exit;
                  }
                  send_NAK();
                  break;
              case 0:
                  // End of transmission
              	eof_cnt++;
              	if (eof_cnt == 1) {
              		send_NAK();
              	}
              	else {
              		send_ACKCRC16();
              	}
                  break;
              default:
                // ** Normal packet **
                if (eof_cnt > 1) {
            		send_ACK();
                }
                else if ((packet_data[PACKET_SEQNO_INDEX] & 0xff) != (packets_received & 0x000000ff)) {
                  errors ++;
                  if (errors > 5) {
                    send_CA();
                    size = -3;
                    sprintf(errmsg, "Wrong packet type received");
                    goto exit;
                  }
                  send_NAK();
                }
                else {
                  if (packets_received == 0) {
                    // ** First packet, Filename packet **
                    if (packet_data[PACKET_HEADER] != 0) {
                      errors = 0;
                      // ** Filename packet has valid data
                      if (getname) {
                        for (i = 0, file_ptr = packet_data + PACKET_HEADER; ((*file_ptr != 0) && (i < 64));) {
                          *getname = *file_ptr++;
                          getname++;
                        }
                        *getname = '\0';
                      }
                      for (i = 0, file_ptr = packet_data + PACKET_HEADER; (*file_ptr != 0) && (i < packet_length);) {
                        file_ptr++;
                      }
                      for (i = 0, file_ptr ++; (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH);) {
                        file_size[i++] = *file_ptr++;
                      }
                      file_size[i++] = '\0';
                      if (strlen(file_size) > 0) size = strtol(file_size, NULL, 10);
                      else size = 0;
  
                      // Test the size of the file
                      if ((size < 1) || (size > maxsize)) {
                        // End session
                        send_CA();
                        if (size > maxsize) size = -9;
                        else size = -4;
                        sprintf(errmsg, "Wrong file size");
                        goto exit;
                      }
  
                      file_len = 0;
                      send_ACKCRC16();
                    }
                    // Filename packet is empty, end session
                    else {
                        errors ++;
                        if (errors > 5) {
                          send_CA();
                          sprintf(errmsg, "Filename packet is empty, end session");
                          size = -5;
                          goto exit;
                        }
                        send_NAK();
                    }
                  }
                  else {
                    // ** Data packet **
                    // Write received data to file
                    if (file_len < size) {
                      file_len += packet_length;  // total bytes received
                      if (file_len > size) {
                      	write_len = packet_length - (file_len - size);
                      	file_len = size;
                      }
                      else write_len = packet_length;
  
                      int written_bytes = fwrite((char*)(packet_data + PACKET_HEADER), 1, write_len, ffd);
                      if (written_bytes != write_len) { //failed
                        /* End session */
                        send_CA();
                        size = -6;
                        sprintf(errmsg, "fwrite() error [%d <> %d]", written_bytes, write_len);
                        goto exit;
                      }
                    }
                    //success
                    errors = 0;
                    send_ACK();
                  }
                  packets_received++;
                }
            }
            break;
          case -2:  // user abort
            send_CA();
            size = -7;
            sprintf(errmsg, "User abort");
            goto exit;
          default: // timeout
            if (eof_cnt > 1) {
          	file_done = 1;
            }
            else {
  			  errors ++;
  			  if (errors > MAX_ERRORS) {
  				send_CA();
  				size = -8;
                  sprintf(errmsg, "Max errors");
  				goto exit;
  			  }
  			  send_CRC16();
            }
        }
        if (file_done != 0) {
      	  session_done = 1;
      	  break;
        }
      }
      if (session_done != 0) break;
    }
  
  exit:
    return size;
  }
  
  //------------------------------------------------------------------------------------
  static void Ymodem_PrepareIntialPacket(uint8_t *data, char *fileName, uint32_t length)
  {
    uint16_t tempCRC;
  
    memset(data, 0, PACKET_SIZE + PACKET_HEADER);
    // Make first three packet
    data[0] = SOH;
    data[1] = 0x00;
    data[2] = 0xff;
    
    // add filename
    sprintf((char *)(data+PACKET_HEADER), "%s", fileName);
  
    //add file site
    sprintf((char *)(data + PACKET_HEADER + strlen((char *)(data+PACKET_HEADER)) + 1), "%d", length);
    data[PACKET_HEADER + strlen((char *)(data+PACKET_HEADER)) +
  	   1 + strlen((char *)(data + PACKET_HEADER + strlen((char *)(data+PACKET_HEADER)) + 1))] = ' ';
    
    // add crc
    tempCRC = crc16(&data[PACKET_HEADER], PACKET_SIZE);
    data[PACKET_SIZE + PACKET_HEADER] = tempCRC >> 8;
    data[PACKET_SIZE + PACKET_HEADER + 1] = tempCRC & 0xFF;
  }
  
  //-------------------------------------------------
  static void Ymodem_PrepareLastPacket(uint8_t *data)
  {
    uint16_t tempCRC;
    
    memset(data, 0, PACKET_SIZE + PACKET_HEADER);
    data[0] = SOH;
    data[1] = 0x00;
    data[2] = 0xff;
    tempCRC = crc16(&data[PACKET_HEADER], PACKET_SIZE);
    //tempCRC = crc16_le(0, &data[PACKET_HEADER], PACKET_SIZE);
    data[PACKET_SIZE + PACKET_HEADER] = tempCRC >> 8;
    data[PACKET_SIZE + PACKET_HEADER + 1] = tempCRC & 0xFF;
  }
  
  //-----------------------------------------------------------------------------------------
  static void Ymodem_PreparePacket(uint8_t *data, uint8_t pktNo, uint32_t sizeBlk, FILE *ffd)
  {
    uint16_t i, size;
    uint16_t tempCRC;
    
    data[0] = STX;
    data[1] = (pktNo & 0x000000ff);
    data[2] = (~(pktNo & 0x000000ff));
  
    size = sizeBlk < PACKET_1K_SIZE ? sizeBlk :PACKET_1K_SIZE;
    // Read block from file
    if (size > 0) {
  	  size = fread(data + PACKET_HEADER, 1, size, ffd);
    }
  
    if ( size  < PACKET_1K_SIZE) {
      for (i = size + PACKET_HEADER; i < PACKET_1K_SIZE + PACKET_HEADER; i++) {
        data[i] = 0x00; // EOF (0x1A) or 0x00
      }
    }
    tempCRC = crc16(&data[PACKET_HEADER], PACKET_1K_SIZE);
    //tempCRC = crc16_le(0, &data[PACKET_HEADER], PACKET_1K_SIZE);
    data[PACKET_1K_SIZE + PACKET_HEADER] = tempCRC >> 8;
    data[PACKET_1K_SIZE + PACKET_HEADER + 1] = tempCRC & 0xFF;
  }
  
  //-------------------------------------------------------------
  static uint8_t Ymodem_WaitResponse(uint8_t ackchr, uint8_t tmo)
  {
    unsigned char receivedC;
    uint32_t errors = 0;
  
    do {
      if (Receive_Byte(&receivedC, NAK_TIMEOUT) == 0) {
        if (receivedC == ackchr) {
          return 1;
        }
        else if (receivedC == CA) {
          send_CA();
          return 2; // CA received, Sender abort
        }
        else if (receivedC == NAK) {
          return 3;
        }
        else {
          return 4;
        }
      }
      else {
        errors++;
      }
    }while (errors < tmo);
    return 0;
  }
  
  
  //---------------------------------------------------------------------------------------
  int Ymodem_Transmit (char* sendFileName, unsigned int sizeFile, FILE *ffd, char *err_msg)
  {
    uint8_t packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD];
    uint16_t blkNumber;
    unsigned char receivedC;
    int err;
    uint32_t size = 0;
  
    // Wait for response from receiver
    err = 0;
    do {
      Send_Byte(CRC16);
    } while (Receive_Byte(&receivedC, NAK_TIMEOUT) < 0 && err++ < 45);
  
    if (err >= 45 || receivedC != CRC16) {
      send_CA();
      sprintf(err_msg, "No response from host");
      return -1;
    }
    
    // === Prepare first block and send it =======================================
    /* When the receiving program receives this block and successfully
     * opened the output file, it shall acknowledge this block with an ACK
     * character and then proceed with a normal YMODEM file transfer
     * beginning with a "C" or NAK tranmsitted by the receiver.
     */
    Ymodem_PrepareIntialPacket(packet_data, sendFileName, sizeFile);
    do 
    {
      // Send Packet
  	  send_Bytes((char *)packet_data, PACKET_SIZE + PACKET_OVERHEAD);
  
  	// Wait for Ack
      err = Ymodem_WaitResponse(ACK, 10);
      if (err == 0 || err == 4) {
        send_CA();
        sprintf(err_msg, "No ACK from host");
        return -2;                  // timeout or wrong response
      }
      else if (err == 2) {
          sprintf(err_msg, "Host abort");
      	return 98; // abort
      }
    }while (err != 1);
  
    // After initial block the receiver sends 'C' after ACK
    if (Ymodem_WaitResponse(CRC16, 10) != 1) {
      send_CA();
      sprintf(err_msg, "No CRC after ACK");
      return -3;
    }
    
    // === Send file blocks ======================================================
    size = sizeFile;
    blkNumber = 0x01;
    
    // Resend packet if NAK  for a count of 10 else end of communication
    while (size)
    {
      // Prepare and send next packet
      Ymodem_PreparePacket(packet_data, blkNumber, size, ffd);
      do
      {
      	send_Bytes((char *)packet_data, PACKET_1K_SIZE + PACKET_OVERHEAD);
  
        // Wait for Ack
        err = Ymodem_WaitResponse(ACK, 10);
        if (err == 1) {
          blkNumber++;
          if (size > PACKET_1K_SIZE) size -= PACKET_1K_SIZE; // Next packet
          else size = 0; // Last packet sent
        }
        else if (err == 0 || err == 4) {
          send_CA();
          sprintf(err_msg, "Timeout or wrong response");
          return -4;                  // timeout or wrong response
        }
        else if (err == 2) {
            sprintf(err_msg, "Host abort");
      	  return -5; // abort
        }
      }while(err != 1);
    }
    
    // === Send EOT ==============================================================
    Send_Byte(EOT); // Send (EOT)
    // Wait for Ack
    do 
    {
      // Wait for Ack
      err = Ymodem_WaitResponse(ACK, 10);
      if (err == 3) {   // NAK
        Send_Byte(EOT); // Send (EOT)
      }
      else if (err == 0 || err == 4) {
        send_CA();
        sprintf(err_msg, "Timeout or wrong response on EOF");
        return -6;                  // timeout or wrong response
      }
      else if (err == 2) {
          sprintf(err_msg, "Host abort on EOT");
      	return -7; // abort
      }
    }while (err != 1);
    
    // === Receiver requests next file, prepare and send last packet =============
    if (Ymodem_WaitResponse(CRC16, 10) != 1) {
  	sprintf(err_msg, "No CRC after EOF");
      send_CA();
      return -8;
    }
  
    Ymodem_PrepareLastPacket(packet_data);
    do 
    {
  	// Send Packet
  	  send_Bytes((char *)packet_data, PACKET_SIZE + PACKET_OVERHEAD);
  
  	// Wait for Ack
      err = Ymodem_WaitResponse(ACK, 10);
      if (err == 0 || err == 4) {
        send_CA();
        sprintf(err_msg, "Timeout or wrong response on last packet");
        return -9;                  // timeout or wrong response
      }
      else if (err == 2) {
          sprintf(err_msg, "Host abort on last packet");
      	return -10; // abort
      }
    }while (err != 1);
    
    return 0; // file transmitted successfully
  }
  
  #endif
  
  // ===== Module methods ===============================================================================
  
  //--------------------------------------------
  STATIC mp_obj_t ymodem_recv(mp_obj_t fname_in)
  {
  #ifdef CONFIG_MICROPY_USE_TELNET
  	if (telnet_loggedin()) {
  		mp_printf(&mp_plat_print, "Cannot execute from Telnet session\n");
  		return mp_const_none;
  	}
  #endif
  
  #if CONFIG_MICROPY_RX_BUFFER_SIZE > 1079
  	if (CONFIG_MICROPY_RX_BUFFER_SIZE < 1080) {
  		mp_printf(&mp_plat_print, "Minimum stdio RX buffer size is 1080 bytes, please rebuild.\n");
  		return mp_const_none;
  	}
  
  	const char *fname = mp_obj_str_get_str(fname_in);
      char fullname[128] = {'\0'};
      int err = 1;
      char err_msg[128] = {'\0'};
      char orig_name[128] = {'\0'};
  
      if (physicalPath(fname, fullname) != 0) {
      	sprintf(err_msg, "File name cannot be resolved");
  		goto exit;
      }
  
  	// Open the file
  	FILE *ffd = fopen(fullname, "wb");
  	if (ffd) {
  		mp_printf(&mp_plat_print, "\nReceiving file, please start YModem transfer on host ...\n");
  		mp_printf(&mp_plat_print, "(Press \"a\" to abort)\n");
  
  		xSemaphoreTake(uart0_mutex, UART_SEMAPHORE_WAIT);
  		uart0_raw_input = 1;
  		xSemaphoreGive(uart0_mutex);
  
  		int rec_res = Ymodem_Receive(ffd, 1000000, orig_name, err_msg);
  
  		xSemaphoreTake(uart0_mutex, UART_SEMAPHORE_WAIT);
  		uart0_raw_input = 0;
  		xSemaphoreGive(uart0_mutex);
  
  		fclose(ffd);
  		mp_printf(&mp_plat_print, "\r\n");
  
  		if (rec_res > 0) {
  			err = 0;
  			mp_printf(&mp_plat_print, "File received, size=%d, original name: \"%s\"\n", rec_res, orig_name);
  		}
  		else remove(fullname);
  	}
  	else {
  		sprintf(err_msg, "Opening file \"%s\" for writing.", fname);
  	}
  
  exit:
  	mp_printf(&mp_plat_print, "\n%s%s\n", ((err == 0) ? "" : "Error: "), err_msg);
  	return mp_const_none;
  #else
  	mp_printf(&mp_plat_print, "Minimum stdin RX buffer size is 1080 bytes, please rebuild.\n");
  	return mp_const_none;
  #endif
  }
  STATIC MP_DEFINE_CONST_FUN_OBJ_1(ymodem_recv_obj, ymodem_recv);
  
  //--------------------------------------------
  STATIC mp_obj_t ymodem_send(mp_obj_t fname_in)
  {
  #ifdef CONFIG_MICROPY_USE_TELNET
  	if (telnet_loggedin()) {
  		mp_printf(&mp_plat_print, "Cannot execute from Telnet session\n");
  		return mp_const_none;
  	}
  #endif
  
  #if CONFIG_MICROPY_RX_BUFFER_SIZE > 1079
      const char *fname = mp_obj_str_get_str(fname_in);
      int fsize = 0, err = 0;
      char fullname[128] = {'\0'};
      char err_msg[128] = {'\0'};
  
      if (physicalPath(fname, fullname) != 0) {
      	sprintf(err_msg, "File name cannot be resolved");
  		goto exit;
      }
  
      // Get file size
  	struct stat buf;
  	int res = stat(fullname, &buf);
  	if (res < 0) {
  		sprintf(err_msg, "Get file size.");
  		goto exit;
  	}
  	fsize = buf.st_size;
  
  	// Open the file
  	FILE *ffd = fopen(fullname, "rb");
  	if (ffd) {
  		mp_printf(&mp_plat_print, "\nSending file, please start YModem receive on host ...\n");
  		mp_printf(&mp_plat_print, "(Press \"a\" to abort)\n");
  
  		xSemaphoreTake(uart0_mutex, UART_SEMAPHORE_WAIT);
  		uart0_raw_input = 1;
  		xSemaphoreGive(uart0_mutex);
  
  		int trans_res = Ymodem_Transmit((char *)fname, fsize, ffd, err_msg);
  
  		xSemaphoreTake(uart0_mutex, UART_SEMAPHORE_WAIT);
  	    uart0_raw_input = 0;
  		xSemaphoreGive(uart0_mutex);
  
  		fclose(ffd);
  		mp_printf(&mp_plat_print, "\r\n");
  		if (trans_res == 0) {
  			err = 0;
  			sprintf(err_msg, "Transfer complete, %d bytes sent", fsize);
  		}
  	}
  	else sprintf(err_msg, "Opening file \"%s\" for reading.", fname);
  
  exit:
  mp_printf(&mp_plat_print, "\n%s%s\n", ((err == 0) ? "" : "Error: "), err_msg);
  	return mp_const_none;
  #else
  	mp_printf(&mp_plat_print, "Minimum stdin RX buffer size is 1080 bytes, please rebuild.\n");
  	return mp_const_none;
  #endif
  }
  STATIC MP_DEFINE_CONST_FUN_OBJ_1(ymodem_send_obj, ymodem_send);
  
  
  
  //--------------------------------------------------------------
  STATIC const mp_rom_map_elem_t ymodem_module_globals_table[] = {
      { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ymodem) },
  
      { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&ymodem_send_obj) },
      { MP_ROM_QSTR(MP_QSTR_recv), MP_ROM_PTR(&ymodem_recv_obj) }
  };
  
  STATIC MP_DEFINE_CONST_DICT(ymodem_module_globals, ymodem_module_globals_table);
  
  //----------------------------------------
  const mp_obj_module_t mp_module_ymodem = {
      .base = { &mp_type_module },
      .globals = (mp_obj_dict_t*)&ymodem_module_globals,
  };
  
  