/* 发出一些简单调制解调器命令
* 需要串行设备的设备名 (最好是拨出设备,
* 或者是非调制解调器控制设备) 作为它唯一的参数.
* 如果你没有可共使用的拨出设备, 那么以CFLAGS_TO_SET取代CLOCAL。
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h> /* 也许需要;和系统有关 */
#include <termios.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#define CFLAGS_TO_SET (CREAD | HUPCL)
#define CFLAGS_TO_CLEAR (CSTOPB | PARENB | CLOCAL)
enum flowmode { NoFlow, HardFlow, SoftFlow };
/* 和系统有关 */
#define CFLAGS_HARDFLOW (CRTSCTS)
#define EXAMPLE_BAUD B19200
#define EXAMPLE_FLOW HardFlow
static void die(const char *msg)
{
fprintf(stderr, "%s\n", msg);
exit(1);
}
static int close_and_complain(int fd, const char *msg, int err)
{
fprintf(stderr, "%s: %s\n", msg, strerror(err));
if (fd >= 0)
close(fd);
errno = err;
return -1;
}
int open_port(const char *name, speed_t baud, enum flowmode flow)
{
int flags;
struct termios attr;
int fd = open(name, O_RDWR | O_NONBLOCK | O_NOCTTY);
if (fd < 0)
return close_and_complain(-1, "open", errno);
/* 设定一些不明确是否敏感的值 */
if (tcgetattr(fd, &attr) < 0)
return close_and_complain(fd, "tcgetattr", errno);
/* 无特殊输入或输出处理 */
attr.c_iflag = (flow == SoftFlow) ? (IXON | IXOFF) : 0;
attr.c_oflag = 0;
/* 设定8位字符宽和一些杂项控制模式 */
attr.c_cflag &= ~(CSIZE | CFLAGS_TO_CLEAR | CFLAGS_HARDFLOW);
attr.c_cflag |= (CS8 | CFLAGS_TO_SET);
if (flow == HardFlow)
attr.c_cflag |= CFLAGS_HARDFLOW;
/* 本机模式 */
attr.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ISIG);
/* 特殊字符 -- 许多已被先前的设定取消 */
{
int i;
#ifdef _POSIX_VDISABLE
attr.c_cc[0] = _POSIX_VDISABLE;
#else
attr.c_cc[0] = fpathconf(fd, _PC_VDISABLE);
#endif
for (i = 1; i < NCCS; i++)
attr.c_cc[i] = attr.c_cc[0];
}
attr.c_cc[VSTART] = 0x11;
attr.c_cc[VSTOP] = 0x13;
/* 对read()函数的计时控制 */
attr.c_cc[VMIN] = 1;
attr.c_cc[VTIME] = 0;
/* 波特律 */
cfsetispeed(&attr, baud);
cfsetospeed(&attr, baud);
/* 写入设定 */
if (tcsetattr(fd, TCSANOW, &attr) < 0)
return close_and_complain(fd, "tcsetattr", errno);
/* 如果系统记住了先前的O_NONBLOCK设定,就取消它 */
flags = fcntl(fd, F_GETFL, 0);
if (flags < 0)
return close_and_complain(fd, "fcntl(GETFL)", errno);
if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0)
return close_and_complain(fd, "fcntl(SETFL)", errno);
return fd;
}
/* 一些简单的计时工具函数 */
/* 向*TV加 SECS 和USECS */
static void timeradd(struct timeval *tv, long secs, long usecs)
{
tv->tv_sec += secs;
if ((tv->tv_usec += usecs) >= 1000000)
{
tv->tv_sec += tv->tv_usec / 1000000;
tv->tv_usec %= 1000000;
}
}
/* 设定 *RES = *A - *B, 返回结果的符号 */
static int timersub(struct timeval *res,
const struct timeval *a, const struct timeval *b)
{
long sec = a->tv_sec - b->tv_sec;
long usec = a->tv_usec - b->tv_usec;
if (usec < 0)
usec += 1000000, --sec;
res->tv_sec = sec;
res->tv_usec = usec;
return (sec < 0) ? (-1) : ((sec == 0 && usec == 0) ? 0 : 1);
}
/* 这个函数不试图处理非正常的字符串 (比如 ababc)
* 超时以微妙计
* 一个更通常的做法是使用alarm()函数处理超时.
* 这个函数为简便起见不使用信号处理并试图提供一种替换方法
*/
int expect(int fd, const char *str, int timeo)
{
int matchlen = 0;
int len = strlen(str);
struct timeval now,end,left;
fd_set fds;
char c;
gettimeofday(&end, NULL);
timeradd(&end, timeo/1000, timeo%1000);
while (matchlen < len)
{
gettimeofday(&now, NULL);
if (timersub(&left, &end, &now) <= 0)
return -1;
FD_ZERO(&fds);
FD_SET(fd, &fds);
if (select(fd+1, &fds, NULL, NULL, &left) <= 0)
return -1;
if (read(fd, &c, 1) != 1)
return -1;
if (isprint((unsigned char)c) || c == '\n' || c == '\r')
putchar(c);
else
printf("\\x%02x", c);
if (c == str[matchlen])
++matchlen;
else
matchlen = 0;
}
return 0;
}
int main(int argc, char **argv)
{
int fd;
unsigned char c;
if (argc < 2)
die("no port specified");
setvbuf(stdout, NULL, _IONBF, 0);
fd = open_port(argv[1], EXAMPLE_BAUD, EXAMPLE_FLOW);
if (fd < 0)
die("cannot open port");
write(fd, "AT\r", 3);
if (expect(fd, "OK", 5000) < 0)
{
write(fd, "AT\r", 3);
if (expect(fd, "OK", 5000) < 0)
{
tcflush(fd, TCIOFLUSH);
close(fd);
die("no response to AT");
}
}
write(fd, "ATI4\r", 5);
expect(fd, "OK", 10000);
putchar('\n');
tcflush(fd, TCIOFLUSH);
close(fd);
return 0;
}