2023-03-03 13:06:45 +01:00
|
|
|
/****************************************************************************
|
|
|
|
* 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 <fcntl.h>
|
|
|
|
#include <stdio.h>
|
2023-04-20 05:53:44 +02:00
|
|
|
#include <getopt.h>
|
|
|
|
#include <libgen.h>
|
|
|
|
#include <pthread.h>
|
2023-03-03 13:06:45 +01:00
|
|
|
#include <sys/stat.h>
|
|
|
|
|
2023-04-20 05:53:44 +02:00
|
|
|
#include <nuttx/mm/circbuf.h>
|
|
|
|
|
2023-03-03 13:06:45 +01:00
|
|
|
#include "ymodem.h"
|
|
|
|
|
|
|
|
/****************************************************************************
|
2023-04-20 05:53:44 +02:00
|
|
|
* Private Type Definitions
|
2023-03-03 13:06:45 +01:00
|
|
|
****************************************************************************/
|
|
|
|
|
2023-04-20 05:53:44 +02:00
|
|
|
struct ymodem_priv_s
|
2023-03-03 13:06:45 +01:00
|
|
|
{
|
2023-04-20 05:53:44 +02:00
|
|
|
int fd;
|
2023-03-03 13:06:45 +01:00
|
|
|
FAR char **filelist;
|
|
|
|
size_t filenum;
|
2023-04-20 05:53:44 +02:00
|
|
|
|
|
|
|
/* Async */
|
|
|
|
|
|
|
|
struct circbuf_s circ;
|
|
|
|
pthread_mutex_t mutex;
|
|
|
|
pthread_cond_t cond;
|
|
|
|
size_t buffersize;
|
|
|
|
pthread_t pid;
|
|
|
|
bool exited;
|
2023-03-03 13:06:45 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Private Functions
|
|
|
|
****************************************************************************/
|
|
|
|
|
2023-04-20 05:53:44 +02:00
|
|
|
static FAR void *async_read(FAR void *arg)
|
2023-03-03 13:06:45 +01:00
|
|
|
{
|
2023-04-20 05:53:44 +02:00
|
|
|
FAR struct ymodem_priv_s *priv = arg;
|
2023-03-03 13:06:45 +01:00
|
|
|
|
2023-04-20 05:53:44 +02:00
|
|
|
pthread_mutex_lock(&priv->mutex);
|
|
|
|
while (priv->exited == false)
|
2023-03-03 13:06:45 +01:00
|
|
|
{
|
2023-04-20 05:53:44 +02:00
|
|
|
FAR uint8_t *buffer;
|
|
|
|
ssize_t ret = 0;
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
if (priv->fd > 0)
|
2023-03-03 13:06:45 +01:00
|
|
|
{
|
2023-04-20 05:53:44 +02:00
|
|
|
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);
|
2023-03-03 13:06:45 +01:00
|
|
|
}
|
2023-04-20 05:53:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2023-03-03 13:06:45 +01:00
|
|
|
|
2023-04-20 05:53:44 +02:00
|
|
|
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);
|
2023-03-03 13:06:45 +01:00
|
|
|
if (ret < 0)
|
|
|
|
{
|
2023-04-20 05:53:44 +02:00
|
|
|
return -errno;
|
2023-03-03 13:06:45 +01:00
|
|
|
}
|
|
|
|
|
2023-04-20 05:53:44 +02:00
|
|
|
priv->fd = open(filename, O_RDONLY);
|
|
|
|
if (priv->fd < 0)
|
2023-03-03 13:06:45 +01:00
|
|
|
{
|
2023-04-20 05:53:44 +02:00
|
|
|
return -errno;
|
2023-03-03 13:06:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
filename = basename(filename);
|
2023-04-20 05:53:44 +02:00
|
|
|
strlcpy(ctx->file_name, filename, PATH_MAX);
|
|
|
|
ctx->file_length = st.st_size;
|
2023-03-03 13:06:45 +01:00
|
|
|
}
|
2023-04-20 05:53:44 +02:00
|
|
|
else if (ctx->packet_type == YMODEM_DATA_PACKET)
|
2023-03-03 13:06:45 +01:00
|
|
|
{
|
2023-04-20 05:53:44 +02:00
|
|
|
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);
|
2023-03-03 13:06:45 +01:00
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
return ret;
|
|
|
|
}
|
2023-04-20 05:53:44 +02:00
|
|
|
|
|
|
|
ctx->file_length -= size;
|
2023-03-03 13:06:45 +01:00
|
|
|
}
|
|
|
|
|
2023-04-20 05:53:44 +02:00
|
|
|
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);
|
2023-03-03 13:06:45 +01:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-04-20 05:53:44 +02:00
|
|
|
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)
|
2023-03-03 13:06:45 +01:00
|
|
|
{
|
|
|
|
fprintf(stderr, "USAGE: %s [OPTIONS] <lname> [<lname> [<lname> ...]]\n",
|
|
|
|
progname);
|
|
|
|
fprintf(stderr, "\nWhere:\n");
|
|
|
|
fprintf(stderr, "\t<lname> is the local file name\n");
|
|
|
|
fprintf(stderr, "\nand OPTIONS include the following:\n");
|
|
|
|
fprintf(stderr,
|
|
|
|
"\t-d <device>: Communication device to use. Default: stdin & stdout\n");
|
2023-04-20 05:53:44 +02:00
|
|
|
fprintf(stderr,
|
|
|
|
"\t-b|--buffersize <size>: Asynchronously send buffer size."
|
|
|
|
"If greater than 0, accept data asynchronously, Default: 0kB\n");
|
2023-03-15 15:29:35 +01:00
|
|
|
fprintf(stderr,
|
|
|
|
"\t-k <size>: Use a custom size to tansfer, Default: 1kB\n");
|
|
|
|
|
2023-04-20 05:53:44 +02:00
|
|
|
exit(EXIT_FAILURE);
|
2023-03-03 13:06:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Public Functions
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
int main(int argc, FAR char *argv[])
|
|
|
|
{
|
2023-04-20 05:53:44 +02:00
|
|
|
struct ymodem_priv_s priv;
|
|
|
|
struct ymodem_ctx_s ctx;
|
|
|
|
FAR char *devname = NULL;
|
|
|
|
int ret = 0;
|
|
|
|
struct option options[] =
|
|
|
|
{
|
|
|
|
{"buffersize", 1, NULL, 'b'},
|
|
|
|
};
|
2023-03-03 13:06:45 +01:00
|
|
|
|
2023-04-20 05:53:44 +02:00
|
|
|
memset(&priv, 0, sizeof(priv));
|
|
|
|
memset(&ctx, 0, sizeof(ctx));
|
2023-03-15 15:29:35 +01:00
|
|
|
while ((ret = getopt_long(argc, argv, "b:d:k:h", options, NULL))
|
2023-04-20 05:53:44 +02:00
|
|
|
!= ERROR)
|
2023-03-03 13:06:45 +01:00
|
|
|
{
|
2023-04-20 05:53:44 +02:00
|
|
|
switch (ret)
|
2023-03-03 13:06:45 +01:00
|
|
|
{
|
2023-04-20 05:53:44 +02:00
|
|
|
case 'b':
|
|
|
|
priv.buffersize = atoi(optarg) * 1024;
|
|
|
|
break;
|
2023-03-03 13:06:45 +01:00
|
|
|
case 'd':
|
2023-04-20 05:53:44 +02:00
|
|
|
devname = optarg;
|
2023-03-03 13:06:45 +01:00
|
|
|
break;
|
2023-03-15 15:29:35 +01:00
|
|
|
case 'k':
|
|
|
|
ctx.custom_size = atoi(optarg) * 1024;
|
|
|
|
if (ctx.custom_size == 0)
|
|
|
|
{
|
|
|
|
show_usage(argv[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2023-03-03 13:06:45 +01:00
|
|
|
case 'h':
|
|
|
|
case '?':
|
2023-04-20 05:53:44 +02:00
|
|
|
default:
|
|
|
|
show_usage(argv[0]);
|
2023-03-03 13:06:45 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-15 15:29:35 +01:00
|
|
|
if (priv.buffersize && ctx.custom_size > priv.buffersize)
|
|
|
|
{
|
|
|
|
show_usage(argv[0]);
|
|
|
|
}
|
|
|
|
|
2023-04-20 05:53:44 +02:00
|
|
|
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;
|
|
|
|
}
|
2023-03-03 13:06:45 +01:00
|
|
|
|
2023-04-20 05:53:44 +02:00
|
|
|
ctx.sendfd = ctx.recvfd;
|
|
|
|
}
|
|
|
|
else
|
2023-03-03 13:06:45 +01:00
|
|
|
{
|
2023-04-20 05:53:44 +02:00
|
|
|
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;
|
|
|
|
}
|
2023-03-03 13:06:45 +01:00
|
|
|
}
|
|
|
|
|
2023-04-20 05:53:44 +02:00
|
|
|
ret = ymodem_send(&ctx);
|
|
|
|
|
|
|
|
if (priv.buffersize)
|
2023-03-03 13:06:45 +01:00
|
|
|
{
|
2023-04-20 05:53:44 +02:00
|
|
|
async_uninit(&priv);
|
2023-03-03 13:06:45 +01:00
|
|
|
}
|
|
|
|
|
2023-04-20 05:53:44 +02:00
|
|
|
out:
|
|
|
|
if (priv.fd > 0)
|
|
|
|
{
|
|
|
|
close(priv.fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx.recvfd > 0)
|
|
|
|
{
|
|
|
|
close(ctx.recvfd);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2023-03-03 13:06:45 +01:00
|
|
|
}
|