raspbootcom.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. /* raspbootcom.c - upload INITRD via serial port to a machine running BOOTBOOT */
  2. /* Copyright (C) 2013 Goswin von Brederlow <goswin-v-b@web.de>
  3. * minor modifications for BOOTBOOT: 2017 bzt (bztsrc@gitlab)
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. */
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <sys/types.h>
  19. #include <sys/stat.h>
  20. #include <fcntl.h>
  21. #include <unistd.h>
  22. #include <string.h>
  23. #include <errno.h>
  24. #include <endian.h>
  25. #include <stdint.h>
  26. #include <termios.h>
  27. #include "../dist/bootboot.h"
  28. #define BUF_SIZE 65536
  29. struct termios old_tio, new_tio;
  30. void do_exit(int fd, int res) {
  31. // close FD
  32. if (fd != -1) close(fd);
  33. // restore settings for STDIN_FILENO
  34. if (isatty(STDIN_FILENO)) {
  35. tcsetattr(STDIN_FILENO,TCSANOW,&old_tio);
  36. }
  37. exit(res);
  38. }
  39. // open serial connection
  40. int open_serial(const char *dev) {
  41. // The termios structure, to be configured for serial interface.
  42. struct termios termios;
  43. // Open the device, read/write, not the controlling tty, and non-blocking I/O
  44. int fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK);
  45. if (fd == -1) {
  46. // failed to open
  47. return -1;
  48. }
  49. // must be a tty
  50. if (!isatty(fd)) {
  51. fprintf(stderr, "%s is not a tty\n", dev);
  52. do_exit(fd, EXIT_FAILURE);
  53. }
  54. // Get the attributes.
  55. if(tcgetattr(fd, &termios) == -1)
  56. {
  57. perror("Failed to get attributes of device");
  58. do_exit(fd, EXIT_FAILURE);
  59. }
  60. // So, we poll.
  61. termios.c_cc[VTIME] = 0;
  62. termios.c_cc[VMIN] = 0;
  63. // 8N1 mode, no input/output/line processing masks.
  64. termios.c_iflag = 0;
  65. termios.c_oflag = 0;
  66. termios.c_cflag = CS8 | CREAD | CLOCAL;
  67. termios.c_lflag = 0;
  68. // Set the baud rate.
  69. if((cfsetispeed(&termios, B115200) < 0) ||
  70. (cfsetospeed(&termios, B115200) < 0))
  71. {
  72. perror("Failed to set baud-rate");
  73. do_exit(fd, EXIT_FAILURE);
  74. }
  75. // Write the attributes.
  76. if (tcsetattr(fd, TCSAFLUSH, &termios) == -1) {
  77. perror("tcsetattr()");
  78. do_exit(fd, EXIT_FAILURE);
  79. }
  80. return fd;
  81. }
  82. // send initrd to rpi
  83. void send_initrd(int fd, const char *file) {
  84. int file_fd;
  85. off_t off;
  86. uint32_t size;
  87. ssize_t pos, total=0;
  88. char *p;
  89. int done = 0;
  90. // Set fd blocking
  91. if (fcntl(fd, F_SETFL, 0) == -1) {
  92. perror("fcntl()");
  93. do_exit(fd, EXIT_FAILURE);
  94. }
  95. // Open file. If not found, simply continue with terminal
  96. if (file==NULL || file[0]==0 || (file_fd = open(file, O_RDONLY)) == -1) {
  97. return;
  98. }
  99. // Get initrd size
  100. off = lseek(file_fd, 0L, SEEK_END);
  101. if (off >= INITRD_MAXSIZE*1024*1024) {
  102. fprintf(stderr, "initrd too big\n");
  103. close(file_fd);
  104. return;
  105. }
  106. // empty file
  107. if (off == 0) {
  108. close(file_fd);
  109. return;
  110. }
  111. size = htole32(off);
  112. lseek(file_fd, 0L, SEEK_SET);
  113. fprintf(stderr, "### sending initrd %s [%zu byte]\n", file, (size_t)off);
  114. // send initrd size to RPi
  115. p = (char*)&size;
  116. pos = 0;
  117. while(pos < 4) {
  118. ssize_t len = write(fd, &p[pos], 4 - pos);
  119. if (len == -1) {
  120. perror("write()");
  121. do_exit(fd, EXIT_FAILURE);
  122. }
  123. pos += len;
  124. }
  125. // wait for OK
  126. char ok_buf[2];
  127. p = ok_buf;
  128. pos = 0;
  129. while(pos < 2) {
  130. ssize_t len = read(fd, &p[pos], 2 - pos);
  131. if (len == -1) {
  132. perror("read()");
  133. do_exit(fd, EXIT_FAILURE);
  134. }
  135. pos += len;
  136. }
  137. if (ok_buf[0] != 'O' || ok_buf[1] != 'K') {
  138. fprintf(stderr, "error after sending size\n");
  139. close(file_fd);
  140. return;
  141. }
  142. while(!done) {
  143. char buf[BUF_SIZE];
  144. ssize_t pos = 0;
  145. ssize_t len = read(file_fd, buf, BUF_SIZE);
  146. switch(len) {
  147. case -1:
  148. perror("read()");
  149. close(file_fd);
  150. return;
  151. case 0:
  152. done = 1;
  153. }
  154. while(len > 0) {
  155. ssize_t len2 = write(fd, &buf[pos], len);
  156. if (len2 == -1) {
  157. perror("write()");
  158. do_exit(fd, EXIT_FAILURE);
  159. }
  160. len -= len2;
  161. pos += len2;
  162. total += len2;
  163. fprintf(stderr,"%3d%% %d / %d\r",total*100/off, total, off);
  164. }
  165. }
  166. close(file_fd);
  167. // Set fd non-blocking
  168. if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
  169. perror("fcntl()");
  170. do_exit(fd, EXIT_FAILURE);
  171. }
  172. fprintf(stderr, "### finished sending\n");
  173. return;
  174. }
  175. int main(int argc, char *argv[]) {
  176. int fd, max_fd = STDIN_FILENO;
  177. fd_set rfds, wfds, efds;
  178. char buf[BUF_SIZE];
  179. size_t start = 0;
  180. size_t end = 0;
  181. int done = 0, leave = 0;
  182. int breaks = 0;
  183. printf("Raspbootcom V1.0 - BOOTBOOT version\n\n");
  184. if (argc < 2) {
  185. printf("USAGE: %s <dev> [file]\n", argv[0]);
  186. printf("Example: %s /dev/ttyUSB0 BOOTBOOT/INITRD\n", argv[0]);
  187. exit(EXIT_FAILURE);
  188. }
  189. // Set STDIN non-blocking and unbuffered
  190. if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) == -1) {
  191. perror("fcntl()");
  192. exit(EXIT_FAILURE);
  193. }
  194. if (isatty(STDIN_FILENO)) {
  195. // get the terminal settings for stdin
  196. if (tcgetattr(STDIN_FILENO, &old_tio) == -1) {
  197. perror("tcgetattr");
  198. exit(EXIT_FAILURE);
  199. }
  200. // we want to keep the old setting to restore them a the end
  201. new_tio=old_tio;
  202. // disable canonical mode (buffered i/o) and local echo
  203. new_tio.c_lflag &= (~ICANON & ~ECHO);
  204. // set the new settings immediately
  205. if (tcsetattr(STDIN_FILENO, TCSANOW, &new_tio) == -1) {
  206. perror("tcsetattr()");
  207. do_exit(-1, EXIT_FAILURE);
  208. }
  209. }
  210. while(!leave) {
  211. // Open device
  212. if ((fd = open_serial(argv[1])) == -1) {
  213. // udev takes a while to change ownership
  214. // so sometimes one gets EPERM
  215. if (errno == ENOENT || errno == ENODEV || errno == EACCES) {
  216. fprintf(stderr, "\r### Waiting for %s...\r", argv[1]);
  217. sleep(1);
  218. continue;
  219. }
  220. perror(argv[1]);
  221. do_exit(fd, EXIT_FAILURE);
  222. }
  223. fprintf(stderr, "### Listening on %s \n", argv[1]);
  224. // select needs the largeds FD + 1
  225. if (fd > STDIN_FILENO) {
  226. max_fd = fd + 1;
  227. } else {
  228. max_fd = STDIN_FILENO + 1;
  229. }
  230. done = 0;
  231. start = end = 0;
  232. while(!done || start != end) {
  233. // Watch stdin and dev for input.
  234. FD_ZERO(&rfds);
  235. if (!done && end < BUF_SIZE) FD_SET(STDIN_FILENO, &rfds);
  236. FD_SET(fd, &rfds);
  237. // Watch fd for output if needed.
  238. FD_ZERO(&wfds);
  239. if (start != end) FD_SET(fd, &wfds);
  240. // Watch stdin and dev for error.
  241. FD_ZERO(&efds);
  242. FD_SET(STDIN_FILENO, &efds);
  243. FD_SET(fd, &efds);
  244. // Wait for something to happend
  245. if (select(max_fd, &rfds, &wfds, &efds, NULL) == -1) {
  246. perror("select()");
  247. do_exit(fd, EXIT_FAILURE);
  248. } else {
  249. // check for errors
  250. if (FD_ISSET(STDIN_FILENO, &efds)) {
  251. fprintf(stderr, "error on STDIN\n");
  252. do_exit(fd, EXIT_FAILURE);
  253. }
  254. if (FD_ISSET(fd, &efds)) {
  255. fprintf(stderr, "error on device\n");
  256. do_exit(fd, EXIT_FAILURE);
  257. }
  258. // RPi is ready to recieve more data, send more
  259. if (FD_ISSET(fd, &wfds)) {
  260. ssize_t len = write(fd, &buf[start], end - start);
  261. if (len == -1) {
  262. perror("write()");
  263. do_exit(fd, EXIT_FAILURE);
  264. }
  265. start += len;
  266. if (start == end) start = end = 0;
  267. // shift buffer contents
  268. if (end == BUF_SIZE) {
  269. memmove(buf, &buf[start], end - start);
  270. end -= start;
  271. start = 0;
  272. }
  273. }
  274. // input from the user, copy to RPi
  275. if (FD_ISSET(STDIN_FILENO, &rfds)) {
  276. ssize_t len = read(STDIN_FILENO, &buf[end], BUF_SIZE - end);
  277. switch(len) {
  278. case -1:
  279. perror("read()");
  280. do_exit(fd, EXIT_FAILURE);
  281. case 0:
  282. done = 1;
  283. leave = 1;
  284. }
  285. end += len;
  286. }
  287. // output from the RPi, copy to STDOUT
  288. if (FD_ISSET(fd, &rfds)) {
  289. char buf2[BUF_SIZE];
  290. ssize_t len = read(fd, buf2, BUF_SIZE);
  291. switch(len) {
  292. case -1:
  293. perror("read()");
  294. do_exit(fd, EXIT_FAILURE);
  295. case 0:
  296. done = 1;
  297. }
  298. // scan output for tripple break (^C^C^C)
  299. // send initrd on tripple break, otherwise output text
  300. const char *p = buf2;
  301. while(p < &buf2[len]) {
  302. const char *q = index(p, '\x03');
  303. if (q == NULL) q = &buf2[len];
  304. if (p == q) {
  305. ++breaks;
  306. ++p;
  307. if (breaks == 3) {
  308. if (start != end) {
  309. fprintf(stderr, "Discarding input after tripple break\n");
  310. start = end = 0;
  311. }
  312. if(argv[2]!=NULL)
  313. send_initrd(fd, argv[2]);
  314. breaks = 0;
  315. }
  316. } else {
  317. while (breaks > 0) {
  318. ssize_t len2 = write(STDOUT_FILENO, "\x03\x03\x03", breaks);
  319. if (len2 == -1) {
  320. perror("write()");
  321. do_exit(fd, EXIT_FAILURE);
  322. }
  323. breaks -= len2;
  324. }
  325. while(p < q) {
  326. ssize_t len2 = write(STDOUT_FILENO, p, q - p);
  327. if (len2 == -1) {
  328. perror("write()");
  329. do_exit(fd, EXIT_FAILURE);
  330. }
  331. p += len2;
  332. }
  333. }
  334. }
  335. }
  336. }
  337. }
  338. close(fd);
  339. }
  340. do_exit(-1, EXIT_SUCCESS);
  341. }