1 /*
2  * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2019-12-09     Steven Liu   the first version
9  * 2021-04-14     Meco Man     Check the file path's legitimacy of 'sy' command
10  */
11 
12 #include <rtthread.h>
13 #include <ymodem.h>
14 #include <dfs_file.h>
15 #include <unistd.h>
16 #include <sys/stat.h>
17 #include <sys/statfs.h>
18 #include <stdlib.h>
19 #include <string.h>
20 
21 #ifndef DFS_USING_POSIX
22 #error "Please enable DFS_USING_POSIX"
23 #endif
24 
25 struct custom_ctx
26 {
27     struct rym_ctx parent;
28     int fd;
29     int flen;
30     char fpath[DFS_PATH_MAX];
31 };
32 
_get_path_lastname(const char * path)33 static const char *_get_path_lastname(const char *path)
34 {
35     char *ptr;
36     if ((ptr = (char *)strrchr(path, '/')) == NULL)
37         return path;
38 
39     /* skip the '/' then return */
40     return ++ptr;
41 }
42 
_rym_recv_begin(struct rym_ctx * ctx,rt_uint8_t * buf,rt_size_t len)43 static enum rym_code _rym_recv_begin(
44     struct rym_ctx *ctx,
45     rt_uint8_t *buf,
46     rt_size_t len)
47 {
48     struct custom_ctx *cctx = (struct custom_ctx *)ctx;
49     char insert_0 = '\0';
50     char *ret;
51     rt_err_t err;
52     ret = strchr(cctx->fpath,insert_0);
53     if(ret)
54     {
55         *ret = '/';
56     }
57     else
58     {
59         rt_kprintf("No end character\n");
60         return RYM_ERR_ACK;
61     }
62     rt_strncpy(ret + 1, (const char *)buf, len - 1);
63     cctx->fd = open(cctx->fpath, O_CREAT | O_WRONLY | O_TRUNC, 0);
64     if (cctx->fd < 0)
65     {
66         err = rt_get_errno();
67         rt_kprintf("error creating file: %d\n", err);
68         return RYM_CODE_CAN;
69     }
70     cctx->flen = atoi(1 + (const char *)buf + rt_strnlen((const char *)buf, len - 1));
71     if (cctx->flen == 0)
72         cctx->flen = -1;
73 
74     return RYM_CODE_ACK;
75 }
76 
_rym_recv_data(struct rym_ctx * ctx,rt_uint8_t * buf,rt_size_t len)77 static enum rym_code _rym_recv_data(
78     struct rym_ctx *ctx,
79     rt_uint8_t *buf,
80     rt_size_t len)
81 {
82     struct custom_ctx *cctx = (struct custom_ctx *)ctx;
83 
84     RT_ASSERT(cctx->fd >= 0);
85     if (cctx->flen == -1)
86     {
87         write(cctx->fd, buf, len);
88     }
89     else
90     {
91         int wlen = len > cctx->flen ? cctx->flen : len;
92         write(cctx->fd, buf, wlen);
93         cctx->flen -= wlen;
94     }
95 
96     return RYM_CODE_ACK;
97 }
98 
_rym_recv_end(struct rym_ctx * ctx,rt_uint8_t * buf,rt_size_t len)99 static enum rym_code _rym_recv_end(
100     struct rym_ctx *ctx,
101     rt_uint8_t *buf,
102     rt_size_t len)
103 {
104     struct custom_ctx *cctx = (struct custom_ctx *)ctx;
105 
106     RT_ASSERT(cctx->fd >= 0);
107     close(cctx->fd);
108     cctx->fd = -1;
109 
110     return RYM_CODE_ACK;
111 }
112 
_rym_send_begin(struct rym_ctx * ctx,rt_uint8_t * buf,rt_size_t len)113 static enum rym_code _rym_send_begin(
114     struct rym_ctx *ctx,
115     rt_uint8_t *buf,
116     rt_size_t len)
117 {
118     struct custom_ctx *cctx = (struct custom_ctx *)ctx;
119     struct stat file_buf;
120     char insert_0 = '\0';
121     rt_err_t err;
122 
123     cctx->fd = open(cctx->fpath, O_RDONLY);
124     if (cctx->fd < 0)
125     {
126         err = rt_get_errno();
127         rt_kprintf("error open file: %d\n", err);
128         return RYM_ERR_FILE;
129     }
130     rt_memset(buf, 0, len);
131     err = stat(cctx->fpath, &file_buf);
132     if (err != RT_EOK)
133     {
134         rt_kprintf("error open file.\n");
135         return RYM_ERR_FILE;
136     }
137 
138     const char *fdst = _get_path_lastname(cctx->fpath);
139     if(fdst != cctx->fpath)
140     {
141         fdst = dfs_normalize_path(RT_NULL, fdst);
142         if (fdst == RT_NULL)
143         {
144             return RYM_ERR_FILE;
145         }
146     }
147 
148     rt_sprintf((char *)buf, "%s%c%d", fdst, insert_0, file_buf.st_size);
149 
150     return RYM_CODE_SOH;
151 }
152 
_rym_send_data(struct rym_ctx * ctx,rt_uint8_t * buf,rt_size_t len)153 static enum rym_code _rym_send_data(
154     struct rym_ctx *ctx,
155     rt_uint8_t *buf,
156     rt_size_t len)
157 {
158     struct custom_ctx *cctx = (struct custom_ctx *)ctx;
159     rt_size_t read_size;
160     int retry_read;
161 
162     read_size = 0;
163     for (retry_read = 0; retry_read < 10; retry_read++)
164     {
165         read_size += read(cctx->fd, buf + read_size, len - read_size);
166         if (read_size == len)
167             break;
168     }
169 
170     if (read_size < len)
171     {
172         rt_memset(buf + read_size, 0x1A, len - read_size);
173         ctx->stage = RYM_STAGE_FINISHING;
174     }
175 
176     if (read_size > 128)
177     {
178         return RYM_CODE_STX;
179     }
180     return RYM_CODE_SOH;
181 }
182 
_rym_send_end(struct rym_ctx * ctx,rt_uint8_t * buf,rt_size_t len)183 static enum rym_code _rym_send_end(
184     struct rym_ctx *ctx,
185     rt_uint8_t *buf,
186     rt_size_t len)
187 {
188     struct custom_ctx *cctx = (struct custom_ctx *)ctx;
189     rt_memset(buf, 0, len);
190     close(cctx->fd);
191     cctx->fd = -1;
192 
193     return RYM_CODE_SOH;
194 }
195 
rym_download_file(rt_device_t idev,const char * file_path)196 static rt_err_t rym_download_file(rt_device_t idev,const char *file_path)
197 {
198     rt_err_t res;
199     struct custom_ctx *ctx = rt_calloc(1, sizeof(*ctx));
200 
201     if (!ctx)
202     {
203         rt_kprintf("rt_malloc failed\n");
204         return -RT_ENOMEM;
205     }
206     ctx->fd = -1;
207     rt_strncpy(ctx->fpath, file_path, DFS_PATH_MAX);
208     RT_ASSERT(idev);
209     res = rym_recv_on_device(&ctx->parent, idev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
210                              _rym_recv_begin, _rym_recv_data, _rym_recv_end, 1000);
211     rt_free(ctx);
212 
213     return res;
214 }
215 
rym_upload_file(rt_device_t idev,const char * file_path)216 static rt_err_t rym_upload_file(rt_device_t idev, const char *file_path)
217 {
218     rt_err_t res = 0;
219 
220     struct custom_ctx *ctx = rt_calloc(1, sizeof(*ctx));
221     if (!ctx)
222     {
223         rt_kprintf("rt_malloc failed\n");
224         return -RT_ENOMEM;
225     }
226     ctx->fd = -1;
227     rt_strncpy(ctx->fpath, file_path, DFS_PATH_MAX);
228     RT_ASSERT(idev);
229     res = rym_send_on_device(&ctx->parent, idev,
230                              RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
231                              _rym_send_begin, _rym_send_data, _rym_send_end, 1000);
232     rt_free(ctx);
233 
234     return res;
235 }
236 
237 #ifdef RT_USING_FINSH
238 #include <finsh.h>
239 
ry(uint8_t argc,char ** argv)240 static rt_err_t ry(uint8_t argc, char **argv)
241 {
242     rt_err_t res;
243     rt_device_t dev;
244     /* temporarily support 1 file*/
245     const char *file_path;
246     if (argc < 2)
247     {
248         rt_kprintf("invalid file path.\n");
249         return -RT_ERROR;
250     }
251     if (argc > 2)
252         dev = rt_device_find(argv[2]);
253     else
254         dev = rt_console_get_device();
255     if (!dev)
256     {
257         rt_kprintf("could not find device.\n");
258         return -RT_ERROR;
259     }
260     file_path = argv[1];
261     res = rym_download_file(dev,file_path);
262 
263     return res;
264 }
265 MSH_CMD_EXPORT(ry, YMODEM Receive e.g: ry file_path [uart0] default by console.);
266 
sy(uint8_t argc,char ** argv)267 static rt_err_t sy(uint8_t argc, char **argv)
268 {
269     rt_err_t res;
270     /* temporarily support 1 file*/
271     const char *file_path;
272     rt_device_t dev;
273 
274     if (argc < 2)
275     {
276         rt_kprintf("invalid file path.\n");
277         return -RT_ERROR;
278     }
279 
280     if (argc > 2)
281         dev = rt_device_find(argv[2]);
282     else
283         dev = rt_console_get_device();
284     if (!dev)
285     {
286         rt_kprintf("could not find device.\n");
287         return -RT_ERROR;
288     }
289     file_path = argv[1];
290     res = rym_upload_file(dev, file_path);
291 
292     return res;
293 }
294 MSH_CMD_EXPORT(sy, YMODEM Send e.g: sy file_path [uart0] default by console.);
295 
296 #endif /* RT_USING_FINSH */
297