mega-usb: allow sending any commands
[megadrive.git] / mega-usb / mega-usb.c
CommitLineData
0a613c3d 1/*
2 * Tool for USB communication with Mega Everdrive
3 * Copyright (c) 2013,2014 notaz
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25
9f5a788d 26#define _BSD_SOURCE
27#include <stdio.h>
28#include <string.h>
29#include <stdlib.h>
30#include <string.h>
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <fcntl.h>
34#include <termios.h>
35#include <unistd.h>
36
37static int setup(int fd)
38{
9f5a788d 39 struct termios tty;
40 int ret;
41
42 memset(&tty, 0, sizeof(tty));
43
44 ret = tcgetattr(fd, &tty);
45 if (ret != 0)
46 {
47 perror("tcgetattr");
48 return 1;
49 }
50
51 tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
52 | INLCR | IGNCR | ICRNL | IXON);
53 tty.c_oflag &= ~OPOST;
54 tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
55 tty.c_cflag &= ~(CSIZE | PARENB);
56 tty.c_cflag |= CS8;
57
58 //tty.c_cc[VMIN] = 1;
59 //tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
60
61 ret = tcsetattr(fd, TCSANOW, &tty);
62 if (ret != 0) {
63 perror("tcsetattr");
64 return ret;
65 }
66
67 return 0;
68}
69
70static int write_to_cart(int fd, const void *data, size_t size)
71{
72 int ret;
73
74 ret = write(fd, data, size);
75 if (ret != size) {
76 perror("write");
77 exit(1);
78 //return -1;
79 }
80
81 return 0;
82}
83
84static int read_from_cart(int fd, void *data, size_t size)
85{
86 int ret;
87
88 ret = read(fd, data, size);
89 if (ret != (int)size) {
90 perror("read");
91 exit(1);
92 //return -1;
93 }
94
95 return 0;
96}
97
98#define send_cmd(fd, cmd) \
0a613c3d 99 write_to_cart(fd, cmd, strlen(cmd))
9f5a788d 100
101static int read_check_byte(int fd, char expect)
102{
103 char r = '0';
104 int ret;
105
106 ret = read_from_cart(fd, &r, 1);
107 if (ret != 0) {
108 fprintf(stderr, "missing response, need '%c'\n", expect);
109 return -1;
110 }
111
112 if (r != expect) {
113 fprintf(stderr, "unexpected response: '%c', need '%c'\n",
114 r, expect);
115 return -1;
116 }
117
118 return 0;
119}
120
121static int write_with_check(int fd, const void *data, size_t size, char chk)
122{
123 int ret;
124
125 ret = write_to_cart(fd, data, size);
126 if (ret)
127 return ret;
128
129 ret = read_check_byte(fd, chk);
130 if (ret != 0) {
131 if (size < 16)
132 fprintf(stderr, "data sent: '%16s'\n",
133 (const char *)data);
134 exit(1);
135 //return -1;
136 }
137
138 return 0;
139}
140
141#define send_cmd_check_k(fd, cmd) \
0a613c3d 142 write_with_check(fd, cmd, strlen(cmd), 'k')
9f5a788d 143
0a613c3d 144static int send_file(int fd, const char *fname, const char *cmd)
9f5a788d 145{
9f5a788d 146 char buf[0x10000];
0a613c3d 147 int retval = -1;
148 FILE *f = NULL;
149 size_t blocksz;
9f5a788d 150 size_t size;
151 int blocks;
9f5a788d 152 int ret;
9f5a788d 153 int i;
154
0a613c3d 155 f = fopen(fname, "rb");
156 if (f == NULL)
157 {
158 fprintf(stderr, "fopen %s: ", fname);
159 return -1;
9f5a788d 160 }
161
0a613c3d 162 fseek(f, 0, SEEK_END);
163 size = ftell(f);
164 fseek(f, 0, SEEK_SET);
165 if (size > 0xf00000) {
166 fprintf(stderr, "size too large: %zd\n", size);
167 goto out;
168 }
9f5a788d 169
0a613c3d 170 if (cmd[1] == 'f')
171 blocksz = 1024;
172 else
173 blocksz = sizeof(buf);
9f5a788d 174
0a613c3d 175 blocks = (size + blocksz - 1) / blocksz;
176
177 send_cmd(fd, cmd);
178 ret = write_with_check(fd, &blocks, 1, 'k');
179 if (ret)
180 return ret;
181
182 // at this point, 'd' will arrive even if we don't
183 // write any more data, what's the point of that?
184 // probably a timeout?
185
186 for (i = 0; i < blocks; i++) {
187 ret = fread(buf, 1, blocksz, f);
188 if (i != blocks - 1 && ret != blocksz) {
189 fprintf(stderr, "failed to read block %d/%d\n",
190 i, blocks);
9f5a788d 191 }
0a613c3d 192 if (ret < blocksz)
193 memset(buf + ret, 0, blocksz - ret);
194
195 write_to_cart(fd, buf, blocksz);
196 }
9f5a788d 197
0a613c3d 198 ret = read_check_byte(fd, 'd');
199 if (ret)
200 goto out;
9f5a788d 201
0a613c3d 202 retval = 0;
203out:
204 if (f != NULL)
205 fclose(f);
9f5a788d 206
0a613c3d 207 return retval;
208}
9f5a788d 209
0a613c3d 210static void usage(const char *argv0)
211{
212 printf("usage:\n"
213 "%s [-d <ttydevice>] [-f <file>] command(s)\n"
214 "known commands for ED OS:\n"
215 " *g - upload game\n"
216 " *w - upload and start OS\n"
217 " *o - upload and start OS\n"
218 " *f - upload (and start?) FPGA firmware\n"
219 " *s - start last ROM (same as pressing start in menu)\n"
220 " *r<x> - run SDRAM contents as mapper x:\n"
221 " s - sms, m - md, o - OS app, c - cd BIOS, M - md10m\n"
222 "upload commands must be followed by -f <file>\n"
223 , argv0);
224 exit(1);
225}
226
227static void invarg(int argc, char *argv[], int arg)
228{
229 if (arg < argc)
230 fprintf(stderr, "invalid arg %d: \"%s\"\n", arg, argv[arg]);
231 else
232 fprintf(stderr, "missing required argument %d\n", arg);
233 exit(1);
234}
235
236int main(int argc, char *argv[])
237{
238 const char *portname = "/dev/ttyUSB0";
239 const char *pending_cmd = NULL;
240 int ret = -1;
241 int arg = 1;
242 int fd;
243
244 if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
245 usage(argv[0]);
9f5a788d 246
0a613c3d 247 if (!strcmp(argv[arg], "-d")) {
248 arg++;
249 if (argv[arg] != NULL)
250 portname = argv[arg];
251 else
252 invarg(argc, argv, arg);
253 arg++;
254 }
9f5a788d 255
0a613c3d 256 fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
257 if (fd < 0) {
258 fprintf(stderr, "open %s: ", portname);
259 perror("");
260 return 1;
9f5a788d 261 }
262
0a613c3d 263 setup(fd);
9f5a788d 264
0a613c3d 265 send_cmd_check_k(fd, " *T");
266
267 for (; arg < argc; arg++) {
268 if (!strcmp(argv[arg], "-f")) {
269 arg++;
270 if (argv[arg] == NULL)
271 invarg(argc, argv, argc);
272 if (pending_cmd == NULL)
273 // assume game upload
274 pending_cmd = "*g";
275
276 ret = send_file(fd, argv[arg], pending_cmd);
277 if (ret)
278 return ret;
279
280 pending_cmd = NULL;
281 continue;
282 }
283 if (argv[arg + 1] && !strcmp(argv[arg + 1], "-f")) {
284 /* we'll avoid sending command if there are
285 * problems with specified file */
286 pending_cmd = argv[arg];
287 continue;
288 }
289
290 ret = send_cmd_check_k(fd, argv[arg]);
291 }
292
293 return ret;
9f5a788d 294}