123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- /* raspbootcom.c - upload INITRD via serial port to a machine running BOOTBOOT */
- /* Copyright (C) 2013 Goswin von Brederlow <goswin-v-b@web.de>
- * minor modifications for BOOTBOOT: 2017 bzt (bztsrc@gitlab)
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <string.h>
- #include <errno.h>
- #include <endian.h>
- #include <stdint.h>
- #include <termios.h>
- #include "../dist/bootboot.h"
- #define BUF_SIZE 65536
- struct termios old_tio, new_tio;
- void do_exit(int fd, int res) {
- // close FD
- if (fd != -1) close(fd);
- // restore settings for STDIN_FILENO
- if (isatty(STDIN_FILENO)) {
- tcsetattr(STDIN_FILENO,TCSANOW,&old_tio);
- }
- exit(res);
- }
- // open serial connection
- int open_serial(const char *dev) {
- // The termios structure, to be configured for serial interface.
- struct termios termios;
- // Open the device, read/write, not the controlling tty, and non-blocking I/O
- int fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK);
- if (fd == -1) {
- // failed to open
- return -1;
- }
- // must be a tty
- if (!isatty(fd)) {
- fprintf(stderr, "%s is not a tty\n", dev);
- do_exit(fd, EXIT_FAILURE);
- }
- // Get the attributes.
- if(tcgetattr(fd, &termios) == -1)
- {
- perror("Failed to get attributes of device");
- do_exit(fd, EXIT_FAILURE);
- }
- // So, we poll.
- termios.c_cc[VTIME] = 0;
- termios.c_cc[VMIN] = 0;
- // 8N1 mode, no input/output/line processing masks.
- termios.c_iflag = 0;
- termios.c_oflag = 0;
- termios.c_cflag = CS8 | CREAD | CLOCAL;
- termios.c_lflag = 0;
- // Set the baud rate.
- if((cfsetispeed(&termios, B115200) < 0) ||
- (cfsetospeed(&termios, B115200) < 0))
- {
- perror("Failed to set baud-rate");
- do_exit(fd, EXIT_FAILURE);
- }
- // Write the attributes.
- if (tcsetattr(fd, TCSAFLUSH, &termios) == -1) {
- perror("tcsetattr()");
- do_exit(fd, EXIT_FAILURE);
- }
- return fd;
- }
- // send initrd to rpi
- void send_initrd(int fd, const char *file) {
- int file_fd;
- off_t off;
- uint32_t size;
- ssize_t pos, total=0;
- char *p;
- int done = 0;
- // Set fd blocking
- if (fcntl(fd, F_SETFL, 0) == -1) {
- perror("fcntl()");
- do_exit(fd, EXIT_FAILURE);
- }
- // Open file. If not found, simply continue with terminal
- if (file==NULL || file[0]==0 || (file_fd = open(file, O_RDONLY)) == -1) {
- return;
- }
- // Get initrd size
- off = lseek(file_fd, 0L, SEEK_END);
- if (off >= INITRD_MAXSIZE*1024*1024) {
- fprintf(stderr, "initrd too big\n");
- close(file_fd);
- return;
- }
- // empty file
- if (off == 0) {
- close(file_fd);
- return;
- }
- size = htole32(off);
- lseek(file_fd, 0L, SEEK_SET);
- fprintf(stderr, "### sending initrd %s [%zu byte]\n", file, (size_t)off);
- // send initrd size to RPi
- p = (char*)&size;
- pos = 0;
- while(pos < 4) {
- ssize_t len = write(fd, &p[pos], 4 - pos);
- if (len == -1) {
- perror("write()");
- do_exit(fd, EXIT_FAILURE);
- }
- pos += len;
- }
- // wait for OK
- char ok_buf[2];
- p = ok_buf;
- pos = 0;
- while(pos < 2) {
- ssize_t len = read(fd, &p[pos], 2 - pos);
- if (len == -1) {
- perror("read()");
- do_exit(fd, EXIT_FAILURE);
- }
- pos += len;
- }
- if (ok_buf[0] != 'O' || ok_buf[1] != 'K') {
- fprintf(stderr, "error after sending size\n");
- close(file_fd);
- return;
- }
- while(!done) {
- char buf[BUF_SIZE];
- ssize_t pos = 0;
- ssize_t len = read(file_fd, buf, BUF_SIZE);
- switch(len) {
- case -1:
- perror("read()");
- close(file_fd);
- return;
- case 0:
- done = 1;
- }
- while(len > 0) {
- ssize_t len2 = write(fd, &buf[pos], len);
- if (len2 == -1) {
- perror("write()");
- do_exit(fd, EXIT_FAILURE);
- }
- len -= len2;
- pos += len2;
- total += len2;
- fprintf(stderr,"%3d%% %d / %d\r",total*100/off, total, off);
- }
- }
- close(file_fd);
- // Set fd non-blocking
- if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
- perror("fcntl()");
- do_exit(fd, EXIT_FAILURE);
- }
- fprintf(stderr, "### finished sending\n");
- return;
- }
- int main(int argc, char *argv[]) {
- int fd, max_fd = STDIN_FILENO;
- fd_set rfds, wfds, efds;
- char buf[BUF_SIZE];
- size_t start = 0;
- size_t end = 0;
- int done = 0, leave = 0;
- int breaks = 0;
- printf("Raspbootcom V1.0 - BOOTBOOT version\n\n");
- if (argc < 2) {
- printf("USAGE: %s <dev> [file]\n", argv[0]);
- printf("Example: %s /dev/ttyUSB0 BOOTBOOT/INITRD\n", argv[0]);
- exit(EXIT_FAILURE);
- }
- // Set STDIN non-blocking and unbuffered
- if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) == -1) {
- perror("fcntl()");
- exit(EXIT_FAILURE);
- }
- if (isatty(STDIN_FILENO)) {
- // get the terminal settings for stdin
- if (tcgetattr(STDIN_FILENO, &old_tio) == -1) {
- perror("tcgetattr");
- exit(EXIT_FAILURE);
- }
- // we want to keep the old setting to restore them a the end
- new_tio=old_tio;
- // disable canonical mode (buffered i/o) and local echo
- new_tio.c_lflag &= (~ICANON & ~ECHO);
- // set the new settings immediately
- if (tcsetattr(STDIN_FILENO, TCSANOW, &new_tio) == -1) {
- perror("tcsetattr()");
- do_exit(-1, EXIT_FAILURE);
- }
- }
- while(!leave) {
- // Open device
- if ((fd = open_serial(argv[1])) == -1) {
- // udev takes a while to change ownership
- // so sometimes one gets EPERM
- if (errno == ENOENT || errno == ENODEV || errno == EACCES) {
- fprintf(stderr, "\r### Waiting for %s...\r", argv[1]);
- sleep(1);
- continue;
- }
- perror(argv[1]);
- do_exit(fd, EXIT_FAILURE);
- }
- fprintf(stderr, "### Listening on %s \n", argv[1]);
- // select needs the largeds FD + 1
- if (fd > STDIN_FILENO) {
- max_fd = fd + 1;
- } else {
- max_fd = STDIN_FILENO + 1;
- }
- done = 0;
- start = end = 0;
- while(!done || start != end) {
- // Watch stdin and dev for input.
- FD_ZERO(&rfds);
- if (!done && end < BUF_SIZE) FD_SET(STDIN_FILENO, &rfds);
- FD_SET(fd, &rfds);
- // Watch fd for output if needed.
- FD_ZERO(&wfds);
- if (start != end) FD_SET(fd, &wfds);
- // Watch stdin and dev for error.
- FD_ZERO(&efds);
- FD_SET(STDIN_FILENO, &efds);
- FD_SET(fd, &efds);
- // Wait for something to happend
- if (select(max_fd, &rfds, &wfds, &efds, NULL) == -1) {
- perror("select()");
- do_exit(fd, EXIT_FAILURE);
- } else {
- // check for errors
- if (FD_ISSET(STDIN_FILENO, &efds)) {
- fprintf(stderr, "error on STDIN\n");
- do_exit(fd, EXIT_FAILURE);
- }
- if (FD_ISSET(fd, &efds)) {
- fprintf(stderr, "error on device\n");
- do_exit(fd, EXIT_FAILURE);
- }
- // RPi is ready to recieve more data, send more
- if (FD_ISSET(fd, &wfds)) {
- ssize_t len = write(fd, &buf[start], end - start);
- if (len == -1) {
- perror("write()");
- do_exit(fd, EXIT_FAILURE);
- }
- start += len;
- if (start == end) start = end = 0;
- // shift buffer contents
- if (end == BUF_SIZE) {
- memmove(buf, &buf[start], end - start);
- end -= start;
- start = 0;
- }
- }
- // input from the user, copy to RPi
- if (FD_ISSET(STDIN_FILENO, &rfds)) {
- ssize_t len = read(STDIN_FILENO, &buf[end], BUF_SIZE - end);
- switch(len) {
- case -1:
- perror("read()");
- do_exit(fd, EXIT_FAILURE);
- case 0:
- done = 1;
- leave = 1;
- }
- end += len;
- }
- // output from the RPi, copy to STDOUT
- if (FD_ISSET(fd, &rfds)) {
- char buf2[BUF_SIZE];
- ssize_t len = read(fd, buf2, BUF_SIZE);
- switch(len) {
- case -1:
- perror("read()");
- do_exit(fd, EXIT_FAILURE);
- case 0:
- done = 1;
- }
- // scan output for tripple break (^C^C^C)
- // send initrd on tripple break, otherwise output text
- const char *p = buf2;
- while(p < &buf2[len]) {
- const char *q = index(p, '\x03');
- if (q == NULL) q = &buf2[len];
- if (p == q) {
- ++breaks;
- ++p;
- if (breaks == 3) {
- if (start != end) {
- fprintf(stderr, "Discarding input after tripple break\n");
- start = end = 0;
- }
- if(argv[2]!=NULL)
- send_initrd(fd, argv[2]);
- breaks = 0;
- }
- } else {
- while (breaks > 0) {
- ssize_t len2 = write(STDOUT_FILENO, "\x03\x03\x03", breaks);
- if (len2 == -1) {
- perror("write()");
- do_exit(fd, EXIT_FAILURE);
- }
- breaks -= len2;
- }
- while(p < q) {
- ssize_t len2 = write(STDOUT_FILENO, p, q - p);
- if (len2 == -1) {
- perror("write()");
- do_exit(fd, EXIT_FAILURE);
- }
- p += len2;
- }
- }
- }
- }
- }
- }
- close(fd);
- }
- do_exit(-1, EXIT_SUCCESS);
- }
|