/* * n64-arduino-serial.c * Communicates with mpkdumper to read and writes memory pak files from Arduino Serial * Written by Mortal (http://www.nintendojo.fr/) and released under WTFPL * * * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE * Version 2, December 2004 * * Copyright (C) 2004 Sam Hocevar * *Everyone is permitted to copy and distribute verbatim or modified *copies of this license document, and changing it is allowed as long *as the name is changed. * * DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE *TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION * * 0. You just DO WHAT THE FUCK YOU WANT TO. * * * */ #include /* Standard input/output definitions */ #include #include /* Standard types */ #include /* Standard bool type */ #include /* String function definitions */ #include /* File control definitions */ #include /* POSIX terminal control definitions */ #include void usage(void); int serialport_init(const char* serialport, int baud); void usage(void) { printf("Usage: mpkdumper -p [OPTIONS]\n" "\n" "Options:\n" " -h, --help Print this help message\n" " -s, --save=file Give a save order to the Arduino\n" " -r, --restore=file Give a restore order to the Arduino\n" ); } int main(int argc, char *argv[]) { int fd_serial = 0, fd_file = 0; char serialport[256], filename[256]; bool save = false; int baudrate = B115200; if (argc==1) { usage(); exit(EXIT_SUCCESS); } /* parse options */ int option_index = 0, opt; static struct option loptions[] = { {"help", no_argument, 0, 'h'}, {"port", required_argument, 0, 'p'}, {"save", required_argument, 0, 's'}, {"restore", required_argument, 0, 'r'} }; while(1) { opt = getopt_long (argc, argv, "hp:s:r:", loptions, &option_index); if (opt==-1) break; switch (opt) { case '0': break; case 'h': usage(); break; case 'p': strcpy(serialport,optarg); fd_serial = serialport_init(serialport, baudrate); usleep (3000 * 1000); // wait for Arduino to reset if(fd_serial==-1) return -1; break; case 's': strcpy(filename,optarg); save = true; break; case 'r': strcpy(filename,optarg); save = false; break; } } // -p option is mandatory if(!fd_serial) { printf("-p option is mandatory"); return -1; } if(save) { //save action is required char command = 's'; char p = '\0'; fd_file = open(filename, O_WRONLY | O_CREAT); if(!fd_file) { printf("Cannot open file for writing !"); return -1; } write(fd_serial, &command, 1); usleep(5*1000); while(1) { // read a char char c; int n = read(fd_serial, &c, 1); if (n < 0) { // nothing read, skipping til next iteration continue; } n = write(fd_file, &c, 1); if (n < 0) { // write error on the file return -1; } if (p == '\n' && c == '\n') { // 2 consecutives \n means EOF close(fd_serial); close(fd_file); return 0; } // saves previous value p = c; } } else { // restore action is required char command = 'r'; char p = '\0'; fd_file = open(filename, O_RDONLY); if (!fd_file) { printf("Cannot open file for reading !"); return -1; } write(fd_serial, &command, 1); usleep(5*1000); while (1) { char c; int n = read(fd_file, &c, 1); if (n <= 0) { printf("Read error\n"); return -1; } // if the line read is empty (just \n) if (p == '\n' && c == '\n') { close(fd_serial); close(fd_file); return 0; } while(write(fd_serial, &c, 1) <= 0) { continue; } p = c; // let the arduino breath (lots of calculation are done on Restore() function if (c == '\n') { usleep(25 * 1000); } } } exit(EXIT_SUCCESS); } // end main // takes the string name of the serial port (e.g. "/dev/tty.usbserial","COM1") // and a baud rate (bps) and connects to that port at that speed and 8N1. // opens the port in fully raw mode so you can send binary data. // returns valid fd, or -1 on error int serialport_init(const char* serialport, int baud) { struct termios toptions; int fd; //fprintf(stderr,"init_serialport: opening port %s @ %d bps\n", // serialport,baud); fd = open(serialport, O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1) { perror("init_serialport: Unable to open port "); return -1; } if (tcgetattr(fd, &toptions) < 0) { perror("init_serialport: Couldn't get term attributes"); return -1; } speed_t brate = baud; // let you override switch below if needed switch(baud) { case 4800: brate=B4800; break; case 9600: brate=B9600; break; #ifdef B14400 case 14400: brate=B14400; break; #endif case 19200: brate=B19200; break; #ifdef B28800 case 28800: brate=B28800; break; #endif case 38400: brate=B38400; break; case 57600: brate=B57600; break; case 115200: brate=B115200; break; } cfsetispeed(&toptions, brate); cfsetospeed(&toptions, brate); toptions.c_cflag =CS8|CREAD|CLOCAL; // turn on READ & ignore ctrl lines toptions.c_iflag = 0; // turn off s/w flow ctrl toptions.c_lflag = 0; toptions.c_oflag = 0; // make raw // see: http://unixwiz.net/techtips/termios-vmin-vtime.html toptions.c_cc[VMIN] = 0; toptions.c_cc[VTIME] = 20; if( tcsetattr(fd, TCSANOW, &toptions) < 0) { perror("init_serialport: Couldn't set term attributes"); return -1; } return fd; }