Explorar el Código

实现ymodem协议的c代码完成, 与SecureCRT联调通过

niujiuru hace 3 días
padre
commit
b0abcd231c
Se han modificado 5 ficheros con 512 adiciones y 0 borrados
  1. 42 0
      ymodem/Makefile
  2. 50 0
      ymodem/ring_buf_test.c
  3. 347 0
      ymodem/ymodem.c
  4. 54 0
      ymodem/ymodem.h
  5. 19 0
      ymodem/ymodem_test.c

+ 42 - 0
ymodem/Makefile

@@ -0,0 +1,42 @@
+.PHONY: build clean
+
+# 头文件
+INCS += -I.
+INCS += -I../swapi
+INCS += -I./include
+
+# 源文件
+SRCS += $(filter-out ../swapi/testLib.c, $(wildcard ../swapi/*.c))
+SRCS += ../swapi/subjects/serial/serial.c ring_buf.c ymodem.c
+
+# .o文件
+OBJS := $(SRCS:.c=.o)
+
+# 编译器
+CC := gcc
+CFLAGS := -Wall -fPIC -O2 -g
+DEFINS := -D_GNU_SOURCE
+
+target ?= x86_64
+ifeq ($(target),armv7hf)
+  CC := arm-linux-gnueabihf-gcc
+	AR := arm-linux-gnueabihf-ar
+endif
+
+# 库文件
+LIBS += -Wl,-Bdynamic -lc -lm -ldl -lpthread
+
+# 编译和清理
+build : libymodem.a ymodem_test.out
+
+%.o : %.c
+	$(CC) $(DEFINS) $(CFLAGS) -c $< $(INCS) -o $@
+
+libymodem.a : $(OBJS)
+	$(AR) -cr $@ $(OBJS)
+
+ymodem_test.out : $(OBJS) ymodem_test.c
+	$(CC) $(DEFINS) $(CFLAGS) $(OBJS) ymodem_test.c $(INCS) $(LIBS) -o $@
+
+clean :
+	rm -rf $(OBJS) *.out *.a config/ log/ status/ var/

+ 50 - 0
ymodem/ring_buf_test.c

@@ -0,0 +1,50 @@
+// 环形队列测试, 单生产者单消费者时线程安全
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include "ring_buf.h"
+
+// 回调函数示例,用于 RingBuf_process_all
+void print_element(RingBufElement el) {
+  printf("Processed element: %u\n", el);
+}
+
+int main(void)
+{
+  RingBufElement storage[8];
+  RingBuf myRing;
+
+  // 1. 初始化环形缓冲区
+  RingBuf_ctor(&myRing, storage, sizeof(storage) / sizeof(RingBufElement));
+
+  // 2. 向环形缓冲区写入
+  for(uint8_t i = 1; i <= 5; ++i) {
+    if (RingBuf_put(&myRing, i)) {
+      printf("Put element: %u\n", i);
+    } else {
+      printf("Buffer full, failed to put: %u\n", i);
+    }
+  }
+
+  // 3. 检查剩余可用空间
+  RingBufCtr free_slots = RingBuf_num_free(&myRing);
+  printf("Number of free slots: %u\n", free_slots);
+
+  // 4. 从环形缓冲区取出
+  RingBufElement val;
+  if(RingBuf_get(&myRing, &val)) {
+    printf("Got element: %u\n", val);
+  } else {
+    printf("Buffer empty, cannot get element\n");
+  }
+
+  // 5. 遍历处理剩余元素
+  printf("Processing all remaining elements:\n");
+  RingBuf_process_all(&myRing, print_element);
+
+  // 6. 检查缓冲区的状态
+  free_slots = RingBuf_num_free(&myRing);
+  printf("Number of free slots after processing: %u\n", free_slots);
+
+  return 0;
+}

+ 347 - 0
ymodem/ymodem.c

@@ -0,0 +1,347 @@
+#include "ymodem.h"
+#include "ring_buf.h"
+#include "../swapi/subjects/serial/serial.h"
+
+static unsigned short crc16(const unsigned char *buf, unsigned long count)
+{
+  unsigned short crc = 0;
+  while(count--) {
+    crc = crc ^ *buf++ << 8;
+    for(int i = 0; i < 8; i++) {
+      if(crc & 0x8000) crc = crc << 1 ^ 0x1021;
+      else crc = crc << 1;
+    }
+  }
+  return crc;
+}
+
+static const char *u32_to_str(unsigned int val)
+{
+  static char num_str[11]; int pos = 10; num_str[10] = 0;
+
+  if(val == 0) return "0"; // If already zero then just return zero
+
+  while((val != 0) && (pos > 0)) {
+    num_str[--pos] = (val % 10) + '0';
+    val /= 10;
+  }
+
+  return &num_str[pos];
+}
+
+static unsigned long str_to_u32(char* str)
+{
+  const char *s = str; unsigned long acc; int c;
+
+  /* Strip leading spaces if any */
+  do {
+    c = *s++;
+  } while(c == ' ');
+
+  for(acc = 0; (c >= '0') && (c <= '9'); c = *s++) {
+    c -= '0'; acc *= 10; acc += c;
+  }
+
+  return acc;
+}
+
+typedef struct
+{
+  void *h; RingBuf rx_buf;
+  RingBufElement rx_buf_storage[2*(PACKET_1K_SIZE+PACKET_OVERHEAD)];
+} SYmodemUart;
+
+static SYmodemUart s_myUart;
+
+static int uart_getchar(int timeout_ms)
+{
+  RingBufElement val; unsigned long stime, now;
+  if(timeout_ms >= 0) xgettickcount(&stime);
+  while(1) {
+    if(RingBuf_get(&s_myUart.rx_buf, &val)) return (int)val;
+    if(timeout_ms >= 0) {
+      xgettickcount(&now);
+      if((now - stime) >= (unsigned long)timeout_ms) return -1; // timeout
+    }
+    sw_thrd_delay(THRDWAIT_DELAY);
+  }
+}
+
+static void uart_putchar(int c)
+{
+  unsigned char ch = (unsigned char)c;
+  serial_send_data(s_myUart.h, &ch, 1);
+}
+
+/* Returns 0 on success, 1 on corrupt packet, -1 on error (timeout): */
+static int receive_packet(char *data, int *length)
+{
+  int i, c; unsigned int packet_size;
+  unsigned short received_crc;
+
+  *length = 0;
+
+  c = uart_getchar(PACKET_TIMEOUT);
+  if(c < 0) return -1;
+
+  switch(c) {
+  case SOH:
+    packet_size = PACKET_SIZE;
+    break;
+  case STX:
+    packet_size = PACKET_1K_SIZE;
+    break;
+  case EOT:
+    return 0;
+  case CAN:
+    c = uart_getchar(PACKET_TIMEOUT);
+    if(c == CAN) { *length = -1; return 0; }
+  default:
+    *length = -1;
+    return 0;
+  }
+
+  *data = (char)c;
+
+  for(i = 1; i < (packet_size + PACKET_OVERHEAD); ++i) {
+    c = uart_getchar(PACKET_TIMEOUT);
+    if(c < 0) return -1;
+    data[i] = (char)c;
+  }
+
+  if(data[PACKET_SEQNO_INDEX] != ((data[PACKET_SEQNO_COMP_INDEX] ^ 0xff) & 0xff)) return 1;
+
+  received_crc = (data[PACKET_HEADER + packet_size] << 8) | data[PACKET_HEADER + packet_size + 1];
+  if(crc16((unsigned char*)data + PACKET_HEADER, packet_size) != received_crc) return 1;
+
+  *length = packet_size;
+  return 0;
+}
+
+static int comio_data_recv_proc(unsigned long wParam/*传递打开的串口句柄*/, unsigned long lParam/*保留暂未使用*/)
+{
+  SYmodemUart *pComIO = &s_myUart; void *pSerial = pComIO->h;
+  const unsigned char *pRecvBuf = serial_get_recv_buffer(pSerial); int nRecvBytes = serial_get_recv_buffer_bytes(pSerial);
+  const char *log_prefix = serial_get_log_prefix(pSerial); RingBufElement val = pRecvBuf[nRecvBytes - 1]; // 1字节数据
+  if(!RingBuf_put(&pComIO->rx_buf, val)) {
+    uart_putchar(CAN); uart_putchar(CAN);
+    sw_log_error("%s: sorry,ring buffer full, failed to put!!!", log_prefix);
+    return -1;
+  }
+  serial_clear_recv_buffer(pSerial);
+  return 1;
+}
+
+int ymodem_recv_files(const char *dir)
+{
+  unsigned char packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD];
+  int packet_length, i, file_done, session_done;
+  unsigned int packets_received, errors, first_try, files_num;
+  char file_name[FILE_NAME_LENGTH], file_size[FILE_SIZE_LENGTH], *file_ptr;
+  unsigned char block[PACKET_1K_SIZE]; unsigned int file_size_val = 0; char path[MAX_PATH_CHARS];
+
+  RingBuf_ctor(&s_myUart.rx_buf, s_myUart.rx_buf_storage, sizeof(s_myUart.rx_buf_storage) / sizeof(RingBufElement));
+  s_myUart.h = serial_open(UART_DEVICE_NAME, UART_BAUD_RATE, UART_PARITY_CHECK, \
+                           comio_data_recv_proc, NULL, NULL);
+  if(!s_myUart.h) return -1;
+
+  file_name[0] = '\0'; first_try = 1; files_num = 0;
+  for(session_done = 0, errors = 0; serial_recvThrd_isAlive(s_myUart.h); )
+  { // receive files
+    if(!first_try) uart_putchar(CRC);
+    first_try = 0;
+
+    for(packets_received = 0, file_done = 0; serial_recvThrd_isAlive(s_myUart.h); )
+    { // receive packets
+      switch(receive_packet((char *)packet_data, &packet_length))
+      {
+      case  0: // receive success
+        if(errors != 0) errors = 0;
+        switch(packet_length)
+        {
+        case -1: { uart_putchar(ACK); serial_close(s_myUart.h, WAITTHRD_SAFEEXIT_TIMEOUT); return -2; } // abort
+        case  0: { uart_putchar(ACK); file_done = 1; ++files_num; break; } // end of file
+        default: // normal data packet
+          if((packet_data[PACKET_SEQNO_INDEX] & 0xff) != (packets_received & 0xff)) { uart_putchar(NAK); break; } // sequence number error
+          if(packets_received == 0)
+          {
+            if(packet_data[PACKET_HEADER] != 0x00) // filename packet has data
+            {
+              for(file_ptr = (char *)packet_data + PACKET_HEADER, i = 0; *file_ptr && i < FILE_NAME_LENGTH; ) file_name[i++] = *file_ptr++;
+              file_name[i++] = '\0'; // 接收的文件名称
+              for(++file_ptr, i = 0; *file_ptr != ' ' && i < FILE_SIZE_LENGTH; ) file_size[i++] = *file_ptr++;
+              file_size[i++] = '\0'; // 接收的文件大小
+              file_size_val = str_to_u32(file_size);
+
+              uart_putchar(ACK); uart_putchar(CRC);
+
+              sw_log_debug("[%s] <文件: %s, 大小: %u> 开始接收...", UART_MODULE_NAME, file_name, file_size_val);
+              snprintf(path, sizeof(path), "%s/%s", dir, file_name);
+              if(sw_file_exists(path)) sw_file_delete(path);
+            }
+            else
+            { // filename packet is empty(空包文件); end of session
+              uart_putchar(ACK); file_done = 1; session_done = 1; break;
+            }
+          }
+          else
+          {
+            for(i = 0; i < packet_length; i++) { block[i] = packet_data[PACKET_HEADER+i]; }
+            uart_putchar(ACK);
+            sw_log_debug("[%s] <文件: %s, 大小: %u> 收到第%u包数据, 数据包大小: %u", UART_MODULE_NAME, \
+                          file_name, file_size_val, packets_received, packet_length);
+
+            if(!sw_dir_exists(dir)) sw_dir_create(dir);
+            if(sw_file_update(path, "ab", (char *)block, packet_length) != packet_length) { uart_putchar(CAN); uart_putchar(CAN); serial_close(s_myUart.h, WAITTHRD_SAFEEXIT_TIMEOUT); return -3; }
+          }
+          ++packets_received;
+        }
+        break;
+      default: // receive error
+        if(packets_received != 0) { if(++errors >= MAX_ERRORS/*many errors*/) { uart_putchar(CAN); uart_putchar(CAN); serial_close(s_myUart.h, WAITTHRD_SAFEEXIT_TIMEOUT); return -4; } }
+        uart_putchar(CRC);
+      }
+
+      if(file_done)  break; // 文件接收完毕
+    }
+
+    if(session_done) break; // 传输会话结束
+  }
+
+  serial_close(s_myUart.h, WAITTHRD_SAFEEXIT_TIMEOUT);
+  return files_num;
+}
+
+static void send_packet(unsigned char *data, int block_no)
+{
+ int count, packet_size; unsigned short crc;
+
+ /* We use a short packet for block 0 - all others are 1K */
+ if(block_no == 0) packet_size = PACKET_SIZE;
+ else packet_size = PACKET_1K_SIZE;
+
+ crc = crc16(data, packet_size);
+ /* 128 byte packets use SOH, 1K use STX */
+ uart_putchar((block_no == 0) ? SOH : STX);
+ uart_putchar(block_no & 0xFF);
+ uart_putchar(~block_no & 0xFF);
+
+ for(count = 0; count < packet_size; count++) uart_putchar(data[count]);
+ uart_putchar((crc >> 8) & 0xFF);
+ uart_putchar(crc & 0xFF);
+}
+
+/* Send block 0 (the filename block). filename might be truncated to fit. */
+static void send_packet0(char *filename, unsigned long size)
+{
+  unsigned long count = 0; unsigned char block[PACKET_SIZE];
+  const char *num;
+
+  if(filename) {
+    while(*filename && (count < PACKET_SIZE-FILE_SIZE_LENGTH-2)) block[count++] = *filename++;
+    block[count++] = 0;
+
+    num = u32_to_str(size);
+    while(*num) block[count++] = *num++;
+  }
+
+  while(count < PACKET_SIZE) block[count++] = 0;
+
+  send_packet(block, 0);
+}
+
+static long send_data_packets(unsigned char *data, unsigned long size)
+{
+  int blockno = 1; unsigned long send_size; int ch, retry = 0;
+  unsigned long total_sent = 0;
+
+  while(size > 0)
+  {
+    if(size > PACKET_1K_SIZE) send_size = PACKET_1K_SIZE;
+    else send_size = size;
+
+    send_packet(data, blockno);
+    ch = uart_getchar(PACKET_TIMEOUT);
+    if(ch == ACK)
+    {
+      blockno++; retry = 0;
+      data += send_size;
+      size -= send_size;
+      total_sent += send_size; // 已发送的字节数
+    }
+    else if(ch == NAK || ch == -1)
+    {
+      retry++;
+      if(retry >= MAX_ERRORS) return -1; // give up after MAX_ERRORS retries
+    }
+    else if(ch == CAN)
+    {
+      uart_putchar(CAN);
+      uart_putchar(CAN);
+      return -2;
+    }
+  }
+
+  retry = 0; do {
+    uart_putchar(EOT);
+    ch = uart_getchar(PACKET_TIMEOUT);
+  } while((ch != ACK) && (ch != -1) && (++retry < MAX_ERRORS));
+
+  if(ch != ACK) return -3;
+
+  // 最后发送一个空包文件, 结束文件接收
+  ch = uart_getchar(PACKET_TIMEOUT);
+  if(ch == CRC)
+  {
+    retry = 0; do {
+    send_packet0(0, 0);
+    ch = uart_getchar(PACKET_TIMEOUT);
+    } while((ch != ACK) && (ch != -1) && (++retry < MAX_ERRORS));
+
+    if(ch != ACK) return -4;
+  }
+
+  return (long)total_sent;
+}
+
+int ymodem_send_file(const char *path)
+{
+  char file_name[FILE_NAME_LENGTH], *file_buf = NULL;
+  int file_size, ch, crc_nak = 1, retry, ret = 0;
+
+  if(!sw_file_exists(path)) return -1;
+  snprintf(file_name, sizeof(file_name), "%s", xGetPathFileName(path));
+  file_size = sw_file_getSize(path);
+  if(file_size <= 0) return -2;
+
+  file_buf = (char*)malloc(file_size);
+  if(!file_buf) return -3;
+
+  if(sw_file_load(path, "rb", file_buf, file_size) != file_size) { ret = -4; goto ret_p; }
+
+  RingBuf_ctor(&s_myUart.rx_buf, s_myUart.rx_buf_storage, sizeof(s_myUart.rx_buf_storage) / sizeof(RingBufElement));
+  s_myUart.h = serial_open(UART_DEVICE_NAME, UART_BAUD_RATE, UART_PARITY_CHECK, \
+                           comio_data_recv_proc, NULL, NULL);
+  if(!s_myUart.h) { ret = -5; goto ret_p; }
+
+  retry = 0; do { ch = uart_getchar(PACKET_TIMEOUT); } while(ch != CRC && ++retry < MAX_ERRORS);
+  if(ch != CRC) { uart_putchar(CAN); uart_putchar(CAN); ret = -6; goto ret_p; }
+
+  retry = 0; do
+  {
+    send_packet0(file_name, (unsigned long)file_size);
+    ch = uart_getchar(PACKET_TIMEOUT);
+    if(ch == ACK)
+    {
+      ch = uart_getchar(PACKET_TIMEOUT);
+      if(ch == CRC) { ret = send_data_packets((unsigned char *)file_buf, file_size); if(ret < 0) { ret += -7; } break; }
+    }
+    else if((ch == CRC) && (crc_nak)) { crc_nak = 0; continue; } // 接收到CRC, 重发一次0包
+    else if((ch != NAK) || (crc_nak)) { break; } // 接收到NAK, 需要重发一次0包
+  } while(++retry < MAX_ERRORS);
+
+ret_p:
+  if(file_buf) free(file_buf);
+  if(s_myUart.h) serial_close(s_myUart.h, WAITTHRD_SAFEEXIT_TIMEOUT); s_myUart.h = NULL;
+  return ret;
+}

+ 54 - 0
ymodem/ymodem.h

@@ -0,0 +1,54 @@
+// Author: NiuJiuRu
+// Email: niujiuru@qq.com
+// Date: 2025-12-20
+#ifndef __YMODEM_H__
+#define __YMODEM_H__
+
+#define PACKET_SEQNO_INDEX      (1)
+#define PACKET_SEQNO_COMP_INDEX (2)
+
+#define PACKET_HEADER           (3)     /* start, block, block-complement */
+#define PACKET_TRAILER          (2)     /* CRC bytes */
+#define PACKET_OVERHEAD         (PACKET_HEADER + PACKET_TRAILER)
+#define PACKET_SIZE             (128)
+#define PACKET_1K_SIZE          (1024)
+#define PACKET_TIMEOUT          (1000)  /* milliseconds */
+
+#define FILE_NAME_LENGTH (64)
+#define FILE_SIZE_LENGTH (16)
+
+/* ASCII control codes: */
+#define SOH (0x01)  /* start of 128-byte data packet */
+#define STX (0x02)  /* start of 1024-byte data packet */
+#define EOT (0x04)  /* end of transmission */
+#define ACK (0x06)  /* receive OK */
+#define NAK (0x15)  /* receiver error; retry */
+#define CAN (0x18)  /* two of these in succession aborts transfer */
+#define CRC (0x43)  /* use in place of first NAK for CRC mode */
+
+/* Number of consecutive receive errors before giving up: */
+#define MAX_ERRORS (30)
+
+#define UART_MODULE_NAME "YMODEM"         /* 串口模块名称 */
+#define UART_DEVICE_NAME  "/dev/ttymxc2"  /* 串口设备名称 */
+#define UART_BAUD_RATE    115200          /* 串口的波特率 */
+#define UART_PARITY_CHECK "none"          /* 串口的校验位 */
+
+#include "../swapi/include_swapiLib.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+// 通过串口使用 YMODEM 协议接收多个文件, >=0 实际成功接收的文件数, <0 接收过程中发生的错误
+int ymodem_recv_files(const char *dir);
+
+// 通过串口使用 YMODEM 协议发送一个文件, >=0 实际成功发送的字节数, <0 发送过程中发生的错误
+int ymodem_send_file(const char *path);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __YMODEM_H__ */

+ 19 - 0
ymodem/ymodem_test.c

@@ -0,0 +1,19 @@
+// +build ignore
+
+#include "ymodem.h"
+
+int main(int argc,char *argv[])
+{
+  // 发送测试
+  int ret; const char *send_file_name = "./test.bin";
+  int send_file_size = sw_file_getSize(send_file_name);
+  if(send_file_size <= 0) return -1;
+  ret = ymodem_send_file(send_file_name);
+  sw_log_info("发送测试 -> File: %s, Size: %d, Sent bytes: %d", send_file_name, send_file_size, ret);
+
+  // 接收测试
+  ret = ymodem_recv_files("./received");
+  sw_log_info("接收测试 <- Received files: %d", ret);
+
+  return 0;
+}