Bringing WiFi to the Cidco Mailstation
at main 250 lines 5.7 kB view raw
1/* 2 * WiFiStation 3 * Serial port program loader 4 * 5 * Copyright (c) 2019-2021 joshua stein <jcs@jcs.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20#include <err.h> 21#include <errno.h> 22#include <fcntl.h> 23#include <poll.h> 24#include <stdio.h> 25#include <stdlib.h> 26#include <string.h> 27#include <signal.h> 28#include <termios.h> 29#include <unistd.h> 30#include <vis.h> 31#include <sys/ioctl.h> 32#include <sys/stat.h> 33 34static int debug = 0; 35static char vbuf[512]; 36static int serial_fd = -1; 37 38void 39usage(void) 40{ 41 fprintf(stderr, "usage: %s [-d] [-s serial speed] <serial device> " 42 "<file>\n", 43 getprogname()); 44 exit(1); 45} 46 47void 48serial_setup(int fd, speed_t speed) 49{ 50 struct termios tty; 51 52 if (ioctl(fd, TIOCEXCL) != 0) 53 err(1, "ioctl(TIOCEXCL)"); 54 if (tcgetattr(fd, &tty) < 0) 55 err(1, "tcgetattr"); 56 57 tty.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */ 58 tty.c_cflag &= ~CSIZE; 59 tty.c_cflag |= CS8; /* 8-bit characters */ 60 tty.c_cflag &= ~PARENB; /* no parity bit */ 61 tty.c_cflag &= ~CSTOPB; /* only need 1 stop bit */ 62 63 /* setup for non-canonical mode */ 64 tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); 65 tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); 66 tty.c_oflag &= ~OPOST; 67 68 /* fetch bytes as they become available */ 69 tty.c_cc[VMIN] = 1; 70 tty.c_cc[VTIME] = 1; 71 72 cfsetspeed(&tty, speed); 73 74 if (tcsetattr(fd, TCSAFLUSH, &tty) != 0) 75 err(1, "tcsetattr"); 76} 77 78size_t 79serial_read(void *buf, size_t nbytes) 80{ 81 size_t ret; 82 83 ret = read(serial_fd, buf, nbytes); 84 85 if (debug && ret > 0) { 86 strvisx(vbuf, buf, ret, VIS_NL | VIS_CSTYLE | VIS_OCTAL); 87 printf("<<< %s\n", vbuf); 88 } 89 90 return ret; 91} 92 93size_t 94serial_write(const void *buf, size_t nbytes) 95{ 96 if (debug) { 97 strvisx(vbuf, buf, nbytes, VIS_NL | VIS_CSTYLE | VIS_OCTAL); 98 printf(">>> %s\n", vbuf); 99 } 100 101 return write(serial_fd, buf, nbytes); 102} 103 104int 105main(int argc, char *argv[]) 106{ 107 FILE *pFile; 108 struct stat sb; 109 struct pollfd pfd[1]; 110 char *fn, *serial_dev = NULL; 111 unsigned char buf[128], b, cksum = 0, rcksum; 112 unsigned int sent = 0, size = 0; 113 int len, rlen, ch; 114 int serial_speed = B115200; 115 116 while ((ch = getopt(argc, argv, "ds:")) != -1) { 117 switch (ch) { 118 case 'd': 119 debug = 1; 120 break; 121 case 's': 122 serial_speed = (unsigned)strtol(optarg, NULL, 0); 123 if (errno) 124 err(1, "invalid serial port speed value"); 125 break; 126 default: 127 usage(); 128 } 129 } 130 argc -= optind; 131 argv += optind; 132 133 if (argc != 2) 134 usage(); 135 136 serial_dev = argv[0]; 137 serial_fd = open(serial_dev, O_RDWR|O_SYNC); 138 if (serial_fd < 0) 139 err(1, "open %s", serial_dev); 140 141 serial_setup(serial_fd, serial_speed); 142 143 fn = argv[1]; 144 pFile = fopen(fn, "rb"); 145 if (!pFile) 146 err(1, "open %s", fn); 147 148 if (fstat(fileno(pFile), &sb) != 0) 149 err(1, "fstat %s", fn); 150 151 /* we're never going to send huge files */ 152 size = (unsigned int)sb.st_size; 153 154 if (debug) 155 printf("sending %s (%d bytes) via %s\n", fn, size, serial_dev); 156 157 /* 158 * spam some newlines since the TTL connection kinda sucks, and ^C in 159 * case the device is in AT$PINS? mode 160 */ 161 serial_write("\r\n\r\n", 4); 162 b = 3; 163 serial_write(&b, 1); 164 165 /* 166 * send AT to get some output since sometimes the first character is 167 * lost and we'll just get 'T', and we need to see a full response to 168 * AT$UPLOAD later 169 */ 170 serial_write("AT\r", 3); 171 pfd[0].fd = serial_fd; 172 pfd[0].events = POLLIN; 173 while (poll(pfd, 1, 100) > 0) 174 serial_read(buf, sizeof(buf)); 175 176 len = snprintf(buf, sizeof(buf), "AT$UPLOAD %d\r", size); 177 serial_write(buf, len); 178 memset(buf, 0, sizeof(buf)); 179 180 /* it will echo, along with an OK */ 181 rlen = 0; 182 while (poll(pfd, 1, 100) > 0) { 183 len = serial_read(buf + rlen, sizeof(buf) - rlen); 184 if (sizeof(buf) - rlen <= 0) 185 break; 186 rlen += len; 187 } 188 189 len = 0; 190 if (sscanf(buf, "AT$UPLOAD %d\r\nOK%n", &rlen, &len) != 1 || len < 10) { 191 strvis(vbuf, buf, VIS_NL | VIS_CSTYLE | VIS_OCTAL); 192 errx(1, "bad response to AT$UPLOAD: %s", vbuf); 193 } 194 195 /* clear out any remaining response so we can read each byte echoed */ 196 pfd[0].fd = serial_fd; 197 pfd[0].events = POLLIN; 198 while (poll(pfd, 1, 100) > 0) 199 serial_read(buf, sizeof(buf)); 200 201 while (sent < size) { 202 b = fgetc(pFile); 203 if (debug) 204 printf("sending: %05d/%05d (0x%x)\n", sent, size, b); 205 serial_write(&b, 1); 206 cksum ^= b; 207 sent++; 208 209 if (sent % 32 == 0 || sent == size) { 210 if (poll(pfd, 1, 1000) < 1) { 211 printf("\n"); 212 errx(1, "failed poll at byte %d/%d", sent, 213 size); 214 } 215 216 if (serial_read(&rcksum, 1) != 1) { 217 printf("\n"); 218 errx(1, "failed read at byte %d/%d", sent, 219 size); 220 } 221 222 if (rcksum == cksum) { 223 if (debug) 224 printf("checksum 0x%x matches\n", 225 (cksum & 0xff)); 226 } else { 227 printf("\n"); 228 errx(1, "failed checksum of byte %d/%d " 229 "(expected 0x%x, received 0x%x)", 230 sent, size, cksum, rcksum); 231 } 232 } 233 234 if (!debug) 235 printf("\rsent: %05d/%05d", sent, size); 236 237 fflush(stdout); 238 } 239 fclose(pFile); 240 241 printf("\n"); 242 243 /* wait for our final OK */ 244 while (poll(pfd, 1, 200) > 0) 245 serial_read(buf, sizeof(buf)); 246 247 close(serial_fd); 248 249 return 0; 250}