Terminal program for MailStation devices
at master 454 lines 8.6 kB view raw
1/* 2 * msTERM 3 * 4 * Copyright (c) 2019 joshua stein <jcs@jcs.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <stdio.h> 20#include <string.h> 21#include <stdlib.h> 22 23#include "mailstation.h" 24#include "logo.h" 25 26unsigned char last_key; 27unsigned char esc; 28unsigned char old_minutes; 29unsigned char obuf_sent_pos; 30unsigned char last_modem_msr; 31 32#define MODEM_MSR_DCD (1 << 7) 33 34int process_keyboard(void); 35void process_input(unsigned char b); 36void update_clock(void); 37void update_f1(void); 38void obuf_flush(void); 39void wifi_hangup(void); 40 41unsigned char source; 42 43enum { 44 STATUSBAR_F1, 45 STATUSBAR_F2, 46 STATUSBAR_F3, 47 STATUSBAR_F4, 48 STATUSBAR_F5, 49 STATUSBAR_INIT, 50}; 51 52void 53obuf_queue(unsigned char *c) 54{ 55 unsigned char x; 56 57 for (x = 0; c[x] != '\0'; x++) 58 obuf[obuf_pos++] = c[x]; 59} 60 61int 62main(void) 63{ 64 unsigned char ms[10]; 65 unsigned char shown_logo; 66 int b, j; 67 68 /* ignore first peekkey() if it returns power button */ 69 last_key = KEY_POWER; 70 esc = 0; 71 putchar_sgr = 0; 72 in_csi = 0; 73 csibuflen = 0; 74 obuf_pos = 0; 75 obuf_sent_pos = 0; 76 debug0 = 0; 77 shown_logo = 0; 78 79 patch_isr(); 80 81 settings_read(); 82 source = setting_default_source; 83 if (source >= SOURCE_LAST) 84 source = SOURCE_WIFI; 85 86 clear_screen_bufs(); 87 clear_screen(); 88 update_statusbar(STATUSBAR_INIT, NULL); 89 90begin: 91 if (source == SOURCE_WIFI) 92 /* call this early to sleep while we draw the logo */ 93 wifi_init(); 94 95 if (!shown_logo) { 96 /* - 1 to ignore final null byte */ 97 for (b = 0; b < sizeof(logo) - 1; b++) { 98 if (b == 0 || logo[b - 1] == '\n') { 99 /* center without wasting space in logo[] */ 100 for (j = 0; j < 14; j++) 101 putchar(' '); 102 } 103 104 putchar(logo[b]); 105 } 106 printf(" v%u\n\n", msTERM_version); 107 shown_logo = 1; 108 } 109 110 old_minutes = 0xff; 111 update_clock(); 112 113 update_f1(); 114 115 switch (source) { 116 case SOURCE_MODEM: 117 modem_init(); 118 119 /* Restore factory configuration 0 */ 120 obuf_queue("AT&F0"); 121 /* Select modulation - V.34, automode, min_rate */ 122 obuf_queue("+MS=11,1,300,"); 123 /* max_rate */ 124 itoa(setting_modem_speed, ms, 10); 125 obuf_queue(ms); 126 /* Turn speaker on */ 127 obuf_queue("M1"); 128 /* Set low speaker volume */ 129 obuf_queue("L0"); 130 /* Allow result codes to DTE */ 131 obuf_queue("Q0"); 132 /* Enable transparent XON/XOFF flow control */ 133 obuf_queue("&K5\r"); 134 break; 135 case SOURCE_WIFI: 136 obuf_queue("\rAT\r"); 137 obuf_flush(); 138 break; 139 } 140 141 obuf_flush(); 142 143 for (;;) { 144 b = process_keyboard(); 145 if (b == KEY_F4) 146 /* we changed sources */ 147 goto begin; 148 149 switch (source) { 150 case SOURCE_MODEM: 151 if (modem_msr() != last_modem_msr) { 152 update_f1(); 153 last_modem_msr = modem_curmsr; 154 } 155 if (modem_lsr() & (1 << 0)) { 156 process_input(modem_read()); 157 continue; 158 } 159 break; 160 case SOURCE_LPT: 161 b = lptrecv(); 162 if (b <= 0xff) { 163 process_input(b & 0xff); 164 continue; 165 } 166 break; 167 case SOURCE_WIFI: 168 b = wifi_read(); 169 if (b != -1) { 170 process_input(b & 0xff); 171 continue; 172 } 173 break; 174 } 175 176 if (obuf_sent_pos != obuf_pos) 177 obuf_flush(); 178 179 update_clock(); 180 } 181 182 return 0; 183} 184 185void 186update_statusbar(char which, char *status, ...) 187{ 188 va_list args; 189 char tstatus[64]; 190 char *result = NULL; 191 unsigned char i, l; 192 193 if (which == STATUSBAR_INIT) { 194 for (i = 0; i < LCD_COLS; i++) 195 putchar_attr(LCD_ROWS - 1, i, 196 ((i + 1) % 13 == 0 ? '|' : ' '), ATTR_REVERSE); 197 return; 198 } 199 200 va_start(args, status); 201 l = vsprintf(tstatus, status, args); 202 va_end(args); 203 204 if (l > sizeof(tstatus)) { 205 new_mail(1); 206 panic(); 207 } 208 209 for (i = 0; i < l; i++) { 210 putchar_attr(LCD_ROWS - 1, (which * 13) + i, tstatus[i], 211 ATTR_REVERSE); 212 } 213} 214 215void 216update_clock(void) 217{ 218 static const char modem_s[] = "Modem"; 219 static const char wifi_s[] = "WiFi "; 220 221 if (rtcminutes == old_minutes) 222 return; 223 224 update_statusbar(STATUSBAR_F4, " %s ", 225 (source == SOURCE_MODEM ? modem_s: wifi_s)); 226 update_statusbar(STATUSBAR_F5, " %02d:%02d ", 227 (rtc10hours * 10) + rtchours, 228 (rtc10minutes * 10) + rtcminutes); 229 230 old_minutes = rtcminutes; 231} 232 233void 234update_f1(void) 235{ 236 unsigned char s = 0; 237 238 if (source == SOURCE_MODEM) { 239 if (modem_curmsr & MODEM_MSR_DCD) 240 update_statusbar(STATUSBAR_F1, " Hangup "); 241 else 242 update_statusbar(STATUSBAR_F1, " No Carrier"); 243 } else if (source == SOURCE_WIFI) { 244 update_statusbar(STATUSBAR_F1, " Hangup "); 245 } 246} 247 248int 249process_keyboard(void) 250{ 251 unsigned char b; 252 253 b = peekkey(); 254 255 /* this breaks key-repeat, but it's needed to debounce */ 256 if (b == 0) 257 last_key = 0; 258 else if (b == last_key) 259 b = 0; 260 else 261 last_key = b; 262 263 if (b == 0) 264 return 0; 265 266 switch (b) { 267 case KEY_POWER: 268 /* not sure why the delay is needed to actually power down */ 269 clear_screen(); 270 delay(200); 271 powerdown(); 272 break; 273 case KEY_F1: 274 if (source == SOURCE_MODEM) { 275 if (modem_curmsr & MODEM_MSR_DCD) 276 modem_hangup(); 277 } else if (source == SOURCE_WIFI) { 278 wifi_hangup(); 279 } 280 break; 281 case KEY_F4: 282 if (source == SOURCE_MODEM) { 283 printf("\nHanging up modem...\n"); 284 modem_hangup(); 285 modem_powerdown(); 286 printf("\nSwitching to WiFiStation...\n"); 287 source = SOURCE_WIFI; 288 } else if (source == SOURCE_WIFI) { 289 printf("\nDisconnecting WiFiStation...\n"); 290 wifi_hangup(); 291 printf("\nSwitching to modem...\n"); 292 source = SOURCE_MODEM; 293 } 294 setting_default_source = source; 295 settings_write(); 296 break; 297 case KEY_MAIN_MENU: 298 /* send escape */ 299 obuf[obuf_pos++] = ESC; 300 break; 301 case KEY_EMAIL: 302 reboot(); 303 break; 304 case KEY_PAGE_UP: 305 obuf[obuf_pos++] = ESC; 306 obuf[obuf_pos++] = '['; 307 obuf[obuf_pos++] = '5'; 308 obuf[obuf_pos++] = '~'; 309 break; 310 case KEY_PAGE_DOWN: 311 obuf[obuf_pos++] = ESC; 312 obuf[obuf_pos++] = '['; 313 obuf[obuf_pos++] = '6'; 314 obuf[obuf_pos++] = '~'; 315 break; 316 case KEY_UP: 317 obuf[obuf_pos++] = ESC; 318 obuf[obuf_pos++] = '['; 319 obuf[obuf_pos++] = 'A'; 320 break; 321 case KEY_DOWN: 322 obuf[obuf_pos++] = ESC; 323 obuf[obuf_pos++] = '['; 324 obuf[obuf_pos++] = 'B'; 325 break; 326 case KEY_LEFT: 327 obuf[obuf_pos++] = ESC; 328 obuf[obuf_pos++] = '['; 329 obuf[obuf_pos++] = 'D'; 330 break; 331 case KEY_RIGHT: 332 obuf[obuf_pos++] = ESC; 333 obuf[obuf_pos++] = '['; 334 obuf[obuf_pos++] = 'C'; 335 break; 336 case KEY_SIZE: 337 redraw_screen(); 338 update_f1(); 339 break; 340 default: 341 if (b >= META_KEY_BEGIN) 342 return 0; 343 344 if (b == '\n') 345 b = '\r'; 346 347 obuf[obuf_pos++] = b; 348 } 349 350 return b; 351} 352 353void 354process_input(unsigned char b) 355{ 356 if (in_csi) { 357 if (csibuflen >= sizeof(csibuf) - 1) { 358 /* going to overflow, dump */ 359 parseCSI(); 360 in_csi = 0; 361 esc = 0; 362 csibuf[0] = '\0'; 363 csibuflen = 0; 364 } 365 366 if (b == ESC) { 367 /* esc, maybe new csi, dump previous */ 368 parseCSI(); 369 in_csi = 0; 370 esc = 1; 371 csibuf[0] = '\0'; 372 csibuflen = 0; 373 } else { 374 csibuf[csibuflen] = b; 375 csibuflen++; 376 parseCSI(); 377 } 378 379 return; 380 } 381 382 switch (b) { 383 case 7: /* visual bell, ping 'new mail' light */ 384 new_mail(1); 385 delay(150); 386 new_mail(0); 387 break; 388 case 9: /* tab */ 389 while ((cursorx + 1) % 8 != 0) 390 putchar(' '); 391 break; 392 case 26: /* ^Z end of ansi */ 393 break; 394 case ESC: /* esc */ 395 if (esc) 396 /* our previous esc is literal */ 397 putchar(b); 398 esc = 1; 399 break; 400 case 91: /* [ */ 401 if (esc) { 402 esc = 0; 403 in_csi = 1; 404 break; 405 } 406 /* fall through */ 407 default: 408 putchar(b); 409 } 410} 411 412void 413obuf_flush(void) 414{ 415 int b; 416 417 while (obuf_sent_pos != obuf_pos) { 418 switch (source) { 419 case SOURCE_MODEM: 420 if (modem_lsr() & (1 << 5)) 421 /* Transmitter Holding Register Empty */ 422 modem_write(obuf[obuf_sent_pos++]); 423 break; 424 case SOURCE_LPT: 425 lptsend(obuf[obuf_sent_pos++]); 426 break; 427 case SOURCE_ECHO: 428 putchar(obuf[obuf_sent_pos++]); 429 break; 430 case SOURCE_WIFI: 431 if (wifi_write(obuf[obuf_sent_pos]) == -1) { 432 if ((b = wifi_read()) != -1) 433 process_input(b & 0xff); 434 } else 435 obuf_sent_pos++; 436 break; 437 } 438 } 439} 440 441void 442wifi_hangup(void) 443{ 444 obuf[obuf_pos++] = '+'; 445 obuf[obuf_pos++] = '+'; 446 obuf[obuf_pos++] = '+'; 447 obuf_flush(); 448 delay(800); 449 obuf[obuf_pos++] = 'A'; 450 obuf[obuf_pos++] = 'T'; 451 obuf[obuf_pos++] = 'H'; 452 obuf[obuf_pos++] = '\r'; 453 obuf_flush(); 454}