diff --git a/README.md b/README.md index a2ea468..f2f033f 100644 --- a/README.md +++ b/README.md @@ -23,3 +23,10 @@ Usage: -R use raw input (for ussd) -r use raw output (for ussd and sms/recv) -s (for sms/recv/status) + -w keep reading after OK (for asynchronous at replies) + +Some modems acknowledge vendor-specific AT commands before returning their +data. Use a post-OK quiet timeout to collect such asynchronous replies, for +example: + + sms_tool -w 1000 -d /dev/ttyUSB2 at "AT+QTEMP" diff --git a/sms_main.c b/sms_main.c index 0fdd7c6..693552c 100644 --- a/sms_main.c +++ b/sms_main.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,7 @@ static void usage() "\t-R use raw input (for ussd)\n" "\t-r use raw output (for ussd and sms/recv)\n" "\t-s (for sms/recv/status)\n" + "\t-w keep reading after OK (for asynchronous at replies)\n" ); exit(2); } @@ -169,14 +171,26 @@ int main(int argc, char* argv[]) int jsonoutput = 0; int debug = 0; int dcs = -1; + int at_wait_ms = 0; - while ((ch = getopt(argc, argv, "b:c:d:Ds:f:jRr")) != -1){ + while ((ch = getopt(argc, argv, "b:c:d:Ds:f:jRrw:")) != -1){ switch (ch) { case 'b': baudrate = atoi(optarg); break; case 'c': dcs = atoi(optarg); break; case 'd': dev = optarg; break; case 'D': debug = 1; break; case 's': storage = optarg; break; + case 'w': + { + char *end = NULL; + long wait = strtol(optarg, &end, 10); + if (*optarg == '\0' || *end != '\0' || wait < 0 || wait > 60000) { + fprintf(stderr, "Invalid post-OK wait: %s\n", optarg); + return 2; + } + at_wait_ms = (int)wait; + break; + } case 'f': dateformat = optarg; break; case 'j': jsonoutput = 1; break; case 'R': rawinput = 1; break; @@ -240,6 +254,10 @@ int main(int argc, char* argv[]) { fprintf(stderr, "failed to make serial port linebuffered\n"); } + if(!strcmp("at", argv[0]) && setvbuf(pfi, NULL, _IONBF, 0)) + { + fprintf(stderr, "failed to make serial port unbuffered\n"); + } char buf[1024]; if (!strcmp("send", argv[0])) @@ -727,7 +745,32 @@ int main(int argc, char* argv[]) if(starts_with("OK", buf)) { if (debug == 1) printf("%s", buf); - exit(0); + if (at_wait_ms == 0) + exit(0); + + alarm(0); + for (;;) { + struct pollfd pfd = { + .fd = fileno(pfi), + .events = POLLIN, + }; + int rc = poll(&pfd, 1, at_wait_ms); + if (rc == 0) + exit(0); + if (rc < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, "poll(%s): %s\n", dev, strerror(errno)); + exit(1); + } + if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { + fprintf(stderr, "serial port closed while waiting for response\n"); + exit(1); + } + if (fgets(buf, sizeof(buf), pfi) == NULL) + exit(1); + printf("%s", buf); + } } if(starts_with("ERROR", buf)) { if (debug == 1)