/**************************************************************************** * apps/system/ymodem/sb_main.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include "ymodem.h" /**************************************************************************** * Private Type Definitions ****************************************************************************/ struct ymodem_priv_s { int fd; FAR char **filelist; size_t filenum; /* Async */ struct circbuf_s circ; pthread_mutex_t mutex; pthread_cond_t cond; size_t buffersize; pthread_t pid; bool exited; }; /**************************************************************************** * Private Functions ****************************************************************************/ static FAR void *async_read(FAR void *arg) { FAR struct ymodem_priv_s *priv = arg; pthread_mutex_lock(&priv->mutex); while (priv->exited == false) { FAR uint8_t *buffer; ssize_t ret = 0; size_t size; if (priv->fd > 0) { buffer = circbuf_get_writeptr(&priv->circ, &size); ret = read(priv->fd, buffer, size); if (ret < 0) { break; } circbuf_writecommit(&priv->circ, ret); } if (circbuf_is_full(&priv->circ) || priv->fd <= 0 || ret == 0) { pthread_cond_wait(&priv->cond, &priv->mutex); } } pthread_mutex_unlock(&priv->mutex); return NULL; } static int read_data(FAR struct ymodem_priv_s *priv, FAR uint8_t *data, size_t size) { ssize_t i = 0; if (priv->buffersize) { pthread_mutex_lock(&priv->mutex); while (i < size) { ssize_t ret = circbuf_read(&priv->circ, data + i, size - i); if (ret < 0) { pthread_mutex_unlock(&priv->mutex); return ret; } if (ret == 0) { ret = read(priv->fd, data + i, size - i); if (ret < 0) { pthread_mutex_unlock(&priv->mutex); return -errno; } } i += ret; } pthread_cond_signal(&priv->cond); pthread_mutex_unlock(&priv->mutex); } else { while (i < size) { ssize_t ret = read(priv->fd, data + i, size - i); if (ret < 0) { return -errno; } i += ret; } } return 0; } static int handler(FAR struct ymodem_ctx_s *ctx) { FAR struct ymodem_priv_s *priv = ctx->priv; int ret; if (ctx->packet_type == YMODEM_FILENAME_PACKET) { FAR char *filename; struct stat st; if (priv->fd > 0) { close(priv->fd); priv->fd = 0; } filename = priv->filelist[priv->filenum++]; if (filename == NULL) { return -ENOENT; } ret = stat(filename, &st); if (ret < 0) { return -errno; } priv->fd = open(filename, O_RDONLY); if (priv->fd < 0) { return -errno; } filename = basename(filename); strlcpy(ctx->file_name, filename, PATH_MAX); ctx->file_length = st.st_size; } else if (ctx->packet_type == YMODEM_DATA_PACKET) { size_t size; if (ctx->file_length > ctx->packet_size) { size = ctx->packet_size; } else { size = ctx->file_length; memset(ctx->data + size, 0x1a, ctx->packet_size - ctx->file_length); } ret = read_data(priv, ctx->data, size); if (ret < 0) { return ret; } ctx->file_length -= size; } return 0; } static int async_init(FAR struct ymodem_priv_s *priv) { int ret; ret = circbuf_init(&priv->circ, NULL, priv->buffersize); if (ret < 0) { fprintf(stderr, "ERROR: circbuf_init error\n"); return ret; } ret = pthread_mutex_init(&priv->mutex, NULL); if (ret > 0) { fprintf(stderr, "ERROR: pthread_mutex_init error\n"); ret = -ret; goto circ_err; } ret = pthread_cond_init(&priv->cond, NULL); if (ret > 0) { fprintf(stderr, "ERROR: pthread_cond_init error\n"); ret = -ret; goto mutex_err; } ret = pthread_create(&priv->pid, NULL, async_read, priv); if (ret < 0) { ret = -ret; goto cond_err; } return ret; cond_err: pthread_cond_destroy(&priv->cond); mutex_err: pthread_mutex_destroy(&priv->mutex); circ_err: circbuf_uninit(&priv->circ); return ret; } static void async_uninit(FAR struct ymodem_priv_s *priv) { pthread_mutex_lock(&priv->mutex); priv->exited = true; pthread_cond_signal(&priv->cond); pthread_mutex_unlock(&priv->mutex); pthread_join(priv->pid, NULL); pthread_cond_destroy(&priv->cond); pthread_mutex_destroy(&priv->mutex); circbuf_uninit(&priv->circ); } static void show_usage(FAR const char *progname) { fprintf(stderr, "USAGE: %s [OPTIONS] [ [ ...]]\n", progname); fprintf(stderr, "\nWhere:\n"); fprintf(stderr, "\t is the local file name\n"); fprintf(stderr, "\nand OPTIONS include the following:\n"); fprintf(stderr, "\t-d : Communication device to use. Default: stdin & stdout\n"); fprintf(stderr, "\t-b|--buffersize : Asynchronously send buffer size." "If greater than 0, accept data asynchronously, Default: 0kB\n"); fprintf(stderr, "\t-k : Use a custom size to tansfer, Default: 1kB\n"); exit(EXIT_FAILURE); } /**************************************************************************** * Public Functions ****************************************************************************/ int main(int argc, FAR char *argv[]) { struct ymodem_priv_s priv; struct ymodem_ctx_s ctx; FAR char *devname = NULL; int ret = 0; struct option options[] = { {"buffersize", 1, NULL, 'b'}, }; memset(&priv, 0, sizeof(priv)); memset(&ctx, 0, sizeof(ctx)); while ((ret = getopt_long(argc, argv, "b:d:k:h", options, NULL)) != ERROR) { switch (ret) { case 'b': priv.buffersize = atoi(optarg) * 1024; break; case 'd': devname = optarg; break; case 'k': ctx.custom_size = atoi(optarg) * 1024; if (ctx.custom_size == 0) { show_usage(argv[0]); } break; case 'h': case '?': default: show_usage(argv[0]); break; } } if (priv.buffersize && ctx.custom_size > priv.buffersize) { show_usage(argv[0]); } ctx.packet_handler = handler; if (devname) { ctx.recvfd = open(devname, O_RDWR); if (ctx.recvfd < 0) { fprintf(stderr, "ERROR: can't open %s\n", devname); return -errno; } ctx.sendfd = ctx.recvfd; } else { ctx.recvfd = STDIN_FILENO; ctx.sendfd = STDOUT_FILENO; } ctx.priv = &priv; priv.filelist = &argv[optind]; if (priv.buffersize) { ret = async_init(&priv); if (ret < 0) { goto out; } } ret = ymodem_send(&ctx); if (priv.buffersize) { async_uninit(&priv); } out: if (priv.fd > 0) { close(priv.fd); } if (ctx.recvfd > 0) { close(ctx.recvfd); } return ret; }