/************************************************************************/ /* KEGS: Apple //gs Emulator */ /* Copyright 2002-2004 by Kent Dickey */ /* */ /* This code is covered by the GNU GPL */ /* */ /* The KEGS web page is kegs.sourceforge.net */ /* You may contact the author at: kadickey@alumni.princeton.edu */ /************************************************************************/ const char rcsid_scc_socket_driver_c[] = "@(#)$KmKId: scc_socket_driver.c,v 1.11 2004-12-06 19:42:09-05 kentd Exp $"; /* This file contains the socket calls */ #include "defc.h" #include "scc.h" #include extern Scc scc_stat[2]; extern int g_serial_modem[]; extern int h_errno; int g_wsastartup_called = 0; /* Usage: scc_socket_init() called to init socket mode */ /* At all times, we try to have a listen running on the incoming socket */ /* If we want to dial out, we close the incoming socket and create a new */ /* outgoing socket. Any hang-up causes the socket to be closed and it will */ /* then re-open on a subsequent call to scc_socket_open */ void scc_socket_init(int port) { Scc *scc_ptr; #ifdef _WIN32 WSADATA wsadata; int ret; if(g_wsastartup_called == 0) { ret = WSAStartup(MAKEWORD(2,0), &wsadata); printf("WSAStartup ret: %d\n", ret); g_wsastartup_called = 1; } #endif scc_ptr = &(scc_stat[port]); scc_ptr->state = 1; /* successful socket */ scc_ptr->sockfd = -1; /* Indicate no socket open yet */ scc_ptr->accfd = -1; /* Indicate no socket open yet */ scc_ptr->rdwrfd = -1; /* Indicate no socket open yet */ scc_ptr->socket_state = -2; /* 0 means talk to "modem" */ /* 1 connected */ scc_ptr->socket_num_rings = 0; scc_ptr->socket_last_ring_dcycs = 0; scc_ptr->dcd = 0; /* 0 means no carrier */ scc_ptr->modem_s0_val = 0; scc_ptr->host_aux1 = sizeof(struct sockaddr_in); scc_ptr->host_handle = malloc(scc_ptr->host_aux1); /* Real init will be done when bytes need to be read/write from skt */ } void scc_socket_maybe_open_incoming(int port, double dcycs) { Scc *scc_ptr; struct sockaddr_in sa_in; int on; int ret; SOCKET sockfd; int inc; inc = 0; scc_ptr = &(scc_stat[port]); if(scc_ptr->sockfd != -1) { /* it's already open, get out */ return; } if(scc_ptr->socket_state < 0) { /* not initialized, get out */ return; } printf("scc socket close being called from socket_open_out\n"); scc_socket_close(port, 0, dcycs); scc_ptr->socket_state = 0; scc_ptr->socket_num_rings = 0; memset(scc_ptr->host_handle, 0, scc_ptr->host_aux1); while(1) { sockfd = socket(AF_INET, SOCK_STREAM, 0); printf("sockfd ret: %d\n", sockfd); if(sockfd == -1) { printf("socket ret: %d, errno: %d\n", sockfd, errno); scc_socket_close(port, 0, dcycs); scc_ptr->socket_state = -1; return; } /* printf("socket ret: %d\n", sockfd); */ on = 1; ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); if(ret < 0) { printf("setsockopt REUSEADDR ret: %d, err:%d\n", ret, errno); scc_socket_close(port, 0, dcycs); scc_ptr->socket_state = -1; return; } memset(&sa_in, 0, sizeof(sa_in)); sa_in.sin_family = AF_INET; sa_in.sin_port = htons(6501 + port + inc); sa_in.sin_addr.s_addr = htonl(INADDR_ANY); ret = bind(sockfd, (struct sockaddr *)&sa_in, sizeof(sa_in)); if(ret >= 0) { ret = listen(sockfd, 1); break; } /* else ret to bind was < 0 */ printf("bind ret: %d, errno: %d\n", ret, errno); inc++; close(sockfd); printf("Trying next port: %d\n", 6501 + port + inc); if(inc >= 10) { printf("Too many retries, quitting\n"); scc_socket_close(port, 0, dcycs); scc_ptr->socket_state = -1; return; } } printf("SCC port %d is at unix port %d\n", port, 6501 + port + inc); scc_ptr->sockfd = sockfd; scc_ptr->state = 1; /* successful socket */ scc_socket_make_nonblock(port, dcycs); } void scc_socket_open_outgoing(int port, double dcycs) { Scc *scc_ptr; struct sockaddr_in sa_in; struct hostent *hostentptr; int on; int ret; SOCKET sockfd; scc_ptr = &(scc_stat[port]); printf("scc socket close being called from socket_open_out\n"); scc_socket_close(port, 0, dcycs); scc_ptr->socket_state = 0; memset(scc_ptr->host_handle, 0, scc_ptr->host_aux1); sockfd = socket(AF_INET, SOCK_STREAM, 0); printf("sockfd ret: %d\n", sockfd); if(sockfd == -1) { printf("socket ret: %d, errno: %d\n", sockfd, errno); scc_socket_close(port, 1, dcycs); return; } /* printf("socket ret: %d\n", sockfd); */ on = 1; ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); if(ret < 0) { printf("setsockopt REUSEADDR ret: %d, err:%d\n", ret, errno); scc_socket_close(port, 1, dcycs); return; } memset(&sa_in, 0, sizeof(sa_in)); sa_in.sin_family = AF_INET; sa_in.sin_port = htons(23); hostentptr = gethostbyname(&scc_ptr->modem_cmd_str[0]); if(hostentptr == 0) { #ifdef _WIN32 fatal_printf("Lookup host %s failed\n", &scc_ptr->modem_cmd_str[0]); #else fatal_printf("Lookup host %s failed, herrno: %d\n", &scc_ptr->modem_cmd_str[0], h_errno); #endif close(sockfd); scc_socket_close(port, 1, dcycs); x_show_alert(0, 0); return; } memcpy(&sa_in.sin_addr.s_addr, hostentptr->h_addr, hostentptr->h_length); /* The above copies the 32-bit internet address into */ /* sin_addr.s_addr. It's in correct network format */ ret = connect(sockfd, (struct sockaddr *)&sa_in, sizeof(sa_in)); if(ret < 0) { printf("connect ret: %d, errno: %d\n", ret, errno); close(sockfd); scc_socket_close(port, 1, dcycs); return; } scc_socket_modem_connect(port, dcycs); scc_ptr->dcd = 1; /* carrier on */ scc_ptr->socket_state = 1; /* talk to socket */ scc_ptr->socket_num_rings = 0; printf("SCC port %d is now outgoing to %s\n", port, &scc_ptr->modem_cmd_str[0]); scc_ptr->sockfd = sockfd; scc_ptr->state = 1; /* successful socket */ scc_socket_make_nonblock(port, dcycs); scc_ptr->rdwrfd = scc_ptr->sockfd; } void scc_socket_make_nonblock(int port, double dcycs) { Scc *scc_ptr; SOCKET sockfd; int ret; #ifdef _WIN32 u_long flags; #else int flags; #endif scc_ptr = &(scc_stat[port]); sockfd = scc_ptr->sockfd; #ifdef _WIN32 flags = 1; ret = ioctlsocket(sockfd, FIONBIO, &flags); if(ret != 0) { printf("ioctlsocket ret: %d\n", ret); } #else flags = fcntl(sockfd, F_GETFL, 0); if(flags == -1) { printf("fcntl GETFL ret: %d, errno: %d\n", flags, errno); scc_socket_close(port, 1, dcycs); scc_ptr->socket_state = -1; return; } ret = fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); if(ret == -1) { printf("fcntl SETFL ret: %d, errno: %d\n", ret, errno); scc_socket_close(port, 1, dcycs); scc_ptr->socket_state = -1; return; } #endif } void scc_socket_change_params(int port) { } void scc_socket_close(int port, int full_close, double dcycs) { Scc *scc_ptr; int rdwrfd; SOCKET sockfd; int i; scc_ptr = &(scc_stat[port]); printf("In scc_socket_close, %d, %d, %f\n", port, full_close, dcycs); rdwrfd = scc_ptr->rdwrfd; if(rdwrfd >= 0) { printf("socket_close: rdwrfd=%d, closing\n", rdwrfd); close(rdwrfd); } sockfd = scc_ptr->sockfd; if(sockfd != -1) { printf("socket_close: sockfd=%d, closing\n", sockfd); close(sockfd); } scc_ptr->modem_cmd_len = 0; scc_ptr->telnet_mode = 0; scc_ptr->telnet_iac = 0; for(i = 0; i < 2; i++) { scc_ptr->telnet_local_mode[i] = 0; scc_ptr->telnet_remote_mode[i] = 0; scc_ptr->telnet_reqwill_mode[i] = 0; scc_ptr->telnet_reqdo_mode[i] = 0; } scc_ptr->rdwrfd = -1; scc_ptr->sockfd = -1; scc_ptr->dcd = 0; scc_ptr->socket_num_rings = 0; if(!full_close) { return; } scc_socket_modem_hangup(port, dcycs); /* and go back to modem mode */ scc_ptr->socket_state = 0; if(g_serial_modem[port]) { scc_ptr->modem_dial_or_acc_mode = 0; } else { scc_ptr->modem_dial_or_acc_mode = 2; /* always accept */ } } void scc_accept_socket(int port, double dcycs) { #ifdef SCC_SOCKETS Scc *scc_ptr; int flags; int num_rings; int rdwrfd; int ret; scc_ptr = &(scc_stat[port]); if(scc_ptr->sockfd == -1) { printf("in accept_socket, call socket_open\n"); scc_socket_maybe_open_incoming(port, dcycs); } if(scc_ptr->sockfd == -1) { return; /* just give up */ } if(scc_ptr->rdwrfd == -1) { rdwrfd = accept(scc_ptr->sockfd, scc_ptr->host_handle, &(scc_ptr->host_aux1)); if(rdwrfd < 0) { return; } flags = 0; ret = 0; #ifndef _WIN32 /* For Linux, we need to set O_NONBLOCK on the rdwrfd */ flags = fcntl(rdwrfd, F_GETFL, 0); if(flags == -1) { printf("fcntl GETFL ret: %d, errno: %d\n", flags,errno); return; } ret = fcntl(rdwrfd, F_SETFL, flags | O_NONBLOCK); if(ret == -1) { printf("fcntl SETFL ret: %d, errno: %d\n", ret, errno); return; } #endif scc_ptr->rdwrfd = rdwrfd; printf("Set port[%d].rdwrfd = %d\n", port, rdwrfd); num_rings = 4; if(scc_ptr->modem_s0_val > 0) { num_rings = scc_ptr->modem_s0_val; } scc_ptr->socket_num_rings = num_rings; scc_ptr->socket_last_ring_dcycs = 0; /* do ring now*/ scc_socket_modem_do_ring(port, dcycs); /* and send some telnet codes */ scc_ptr->telnet_reqwill_mode[0] = 0xa; /* 3=GO_AH and 1=ECHO */ scc_ptr->telnet_reqdo_mode[0] = 0xa; /* 3=GO_AH and 1=ECHO */ #if 0 scc_ptr->telnet_reqdo_mode[1] = 0x4; /* 34=LINEMODE */ #endif } #endif } void scc_socket_telnet_reqs(int port, double dcycs) { Scc *scc_ptr; word32 mask, willmask, domask; int i, j; scc_ptr = &(scc_stat[port]); for(i = 0; i < 64; i++) { j = i >> 5; mask = 1 << (i & 31); willmask = scc_ptr->telnet_reqwill_mode[j]; if(willmask & mask) { scc_add_to_writebuf(port, 0xff, dcycs); scc_add_to_writebuf(port, 0xfb, dcycs); /* WILL */ scc_add_to_writebuf(port, i, dcycs); } domask = scc_ptr->telnet_reqdo_mode[j]; if(domask & mask) { scc_add_to_writebuf(port, 0xff, dcycs); scc_add_to_writebuf(port, 0xfd, dcycs); /* DO */ scc_add_to_writebuf(port, i, dcycs); } } } void scc_socket_fill_readbuf(int port, int space_left, double dcycs) { #ifdef SCC_SOCKETS byte tmp_buf[256]; Scc *scc_ptr; int rdwrfd; int ret; int i; scc_ptr = &(scc_stat[port]); scc_accept_socket(port, dcycs); scc_socket_modem_do_ring(port, dcycs); if(scc_ptr->socket_state == 0 && g_serial_modem[port]) { /* Just get out, this is modem mode */ /* The transmit function stuffs any bytes in receive buf */ return; } rdwrfd = scc_ptr->rdwrfd; if(rdwrfd < 0) { return; /* just get out */ } /* Try reading some bytes */ space_left = MIN(space_left, 256); ret = recv(rdwrfd, tmp_buf, space_left, 0); if(ret > 0) { for(i = 0; i < ret; i++) { if(tmp_buf[i] == 0) { /* Skip null chars */ continue; } scc_socket_recvd_char(port, tmp_buf[i], dcycs); } } else if(ret == 0) { /* assume socket close */ printf("recv got 0 from rdwrfd=%d, closing\n", rdwrfd); scc_socket_close(port, 1, dcycs); } #endif } int g_scc_dbg_print_cnt = 50; void scc_socket_recvd_char(int port, int c, double dcycs) { Scc *scc_ptr; word32 locmask, remmask, mask; word32 reqwillmask, reqdomask; int telnet_mode, telnet_iac; int eff_c, cpos; int reply; scc_ptr = &(scc_stat[port]); scc_socket_maybe_open_incoming(port, dcycs); telnet_mode = scc_ptr->telnet_mode; telnet_iac = scc_ptr->telnet_iac; if(c >= 0xf0 || telnet_mode || telnet_iac) { g_scc_dbg_print_cnt = 50; } #if 0 if(g_scc_dbg_print_cnt) { printf("Recv: %02x mode: %d\n", c, telnet_mode); g_scc_dbg_print_cnt--; } #endif eff_c = c; if(telnet_iac == 0) { if(c == 0xff) { scc_ptr->telnet_iac = 0xff; return; /* and just get out */ } } else { /* last char was 0xff, see if this is 0xff */ if(c != 0xff) { /* this is some kind of command */ eff_c = eff_c | 0x100; /* indicate prev char IAC */ } } scc_ptr->telnet_iac = 0; mask = 1 << (c & 31); cpos = (c >> 5) & 1; locmask = scc_ptr->telnet_local_mode[cpos] & mask; remmask = scc_ptr->telnet_remote_mode[cpos] & mask; reqwillmask = scc_ptr->telnet_reqwill_mode[cpos] & mask; reqdomask = scc_ptr->telnet_reqdo_mode[cpos] & mask; switch(telnet_mode) { case 0: /* just passing through bytes */ switch(eff_c) { case 0x1fe: /* DON'T */ case 0x1fd: /* DO */ case 0x1fc: /* WON'T */ case 0x1fb: /* WILL */ case 0x1fa: /* SB */ telnet_mode = c; break; default: if(eff_c < 0x100) { scc_add_to_readbuf(port, c, dcycs); } break; } break; case 3: /* LINEMODE SB SLC, octet 0 */ if(eff_c == 0x1f0) { /* SE, the end */ telnet_mode = 0; } printf("LINEMODE SLC octet 0: %02x\n", c); telnet_mode = 4; break; case 4: /* LINEMODE SB SLC, octet 1 */ printf("LINEMODE SLC octet 1: %02x\n", c); telnet_mode = 5; if(eff_c == 0x1f0) { /* SE, the end */ printf("Got SE at octet 1...\n"); telnet_mode = 0; } break; case 5: /* LINEMODE SB SLC, octet 2 */ printf("LINEMODE SLC octet 2: %02x\n", c); telnet_mode = 3; if(eff_c == 0x1f0) { /* SE, the end */ printf("Got SE at octet 2...\n"); telnet_mode = 0; } break; case 34: /* LINEMODE SB beginning */ switch(c) { case 3: /* SLC */ telnet_mode = 3; break; default: telnet_mode = 0xee; /* go to SB eat */ break; } break; case 0xfa: /* in 0xfa = SB mode, eat up subcommands */ switch(c) { case 34: /* LINEMODE */ telnet_mode = 34; break; default: telnet_mode = 0xee; /* SB eat mode */ break; } break; case 0xee: /* in SB eat mode */ if(eff_c == 0x1f0) { /* SE, end of sub-command */ telnet_mode = 0; } else { /* just stay in eat mode */ } break; case 0xfe: /* previous char was "DON'T" */ if(locmask && (reqwillmask == 0)) { /* it's a mode change */ /* always OK to turn off a mode that we had on */ scc_add_to_writebuf(port, 0xff, dcycs); scc_add_to_writebuf(port, 0xfc, dcycs); /* WON'T */ scc_add_to_writebuf(port, c, dcycs); } scc_ptr->telnet_local_mode[cpos] &= ~mask; scc_ptr->telnet_reqwill_mode[cpos] &= ~mask; telnet_mode = 0; break; case 0xfd: /* previous char was "DO" */ reply = 0xfc; if(locmask == 0 && (reqwillmask == 0)) { /* it's a mode change, send some response */ reply = 0xfc; /* nack it with WON'T */ if(c == 0x03 || c == 0x01) { reply = 0xfb; /* Ack with WILL */ } scc_add_to_writebuf(port, 0xff, dcycs); scc_add_to_writebuf(port, reply, dcycs); scc_add_to_writebuf(port, c, dcycs); } if(reqwillmask || (reply == 0xfb)) { scc_ptr->telnet_local_mode[cpos] |= mask; } scc_ptr->telnet_reqwill_mode[cpos] &= ~mask; telnet_mode = 0; break; case 0xfc: /* previous char was "WON'T" */ if(remmask && (reqdomask == 0)) { /* it's a mode change, ack with DON'T */ scc_add_to_writebuf(port, 0xff, dcycs); scc_add_to_writebuf(port, 0xfe, dcycs); /* DON'T */ scc_add_to_writebuf(port, c, dcycs); } scc_ptr->telnet_remote_mode[cpos] &= ~mask; scc_ptr->telnet_reqdo_mode[cpos] &= ~mask; telnet_mode = 0; break; case 0xfb: /* previous char was "WILL" */ reply = 0xfe; /* nack it with DON'T */ if(remmask == 0 && (reqdomask == 0)) { /* it's a mode change, send some response */ if(c == 0x03 || c == 0x01) { reply = 0xfd; /* Ack with DO */ } scc_add_to_writebuf(port, 0xff, dcycs); scc_add_to_writebuf(port, reply, dcycs); scc_add_to_writebuf(port, c, dcycs); } if(reqdomask || (reply == 0xfd)) { scc_ptr->telnet_remote_mode[cpos] |= mask; } scc_ptr->telnet_reqdo_mode[cpos] &= ~mask; telnet_mode = 0; break; default: telnet_mode = 0; break; } scc_ptr->telnet_mode = telnet_mode; } void scc_socket_empty_writebuf(int port, double dcycs) { #ifdef SCC_SOCKETS # ifndef _WIN32 struct sigaction newact, oldact; # endif Scc *scc_ptr; double diff_dcycs; int plus_mode; int rdptr; int wrptr; int rdwrfd; int done; int ret; int len; int c; int i; scc_ptr = &(scc_stat[port]); /* See if +++ done and we should go to command mode */ diff_dcycs = dcycs - scc_ptr->out_char_dcycs; if((diff_dcycs > 900.0*1000) && (scc_ptr->modem_plus_mode == 3) && (scc_ptr->socket_state >= 1) && (g_serial_modem[port] != 0)) { scc_ptr->socket_state = 0; /* go modem mode, stay connect*/ scc_ptr->modem_plus_mode = 0; scc_socket_send_modem_code(port, 0, dcycs); } /* Try writing some bytes */ done = 0; while(!done) { rdptr = scc_ptr->out_rdptr; wrptr = scc_ptr->out_wrptr; if(rdptr == wrptr) { done = 1; break; } rdwrfd = scc_ptr->rdwrfd; len = wrptr - rdptr; if(len < 0) { len = SCC_OUTBUF_SIZE - rdptr; } if(len > 32) { len = 32; } if(len <= 0) { done = 1; break; } if(scc_ptr->socket_state < 1 && g_serial_modem[port]) { len = 1; scc_socket_modem_write(port, scc_ptr->out_buf[rdptr], dcycs); ret = 1; } else { if(rdwrfd == -1) { if(g_serial_modem[port]) { printf("socket_state: %d, ser_mod: %d, " "rdwrfd: %d\n", scc_ptr->socket_state, g_serial_modem[port], rdwrfd); } scc_ptr->socket_state = 0; scc_socket_maybe_open_incoming(port, dcycs); return; } for(i = 0; i < len; i++) { c = scc_ptr->out_buf[rdptr + i]; plus_mode = scc_ptr->modem_plus_mode; diff_dcycs = dcycs - scc_ptr->out_char_dcycs; if(c == '+' && plus_mode == 0) { if(diff_dcycs > 500*1000) { scc_ptr->modem_plus_mode = 1; } } else if(c == '+') { if(diff_dcycs < 800.0*1000) { plus_mode++; scc_ptr->modem_plus_mode = plus_mode; } } else { scc_ptr->modem_plus_mode = 0; } scc_ptr->out_char_dcycs = dcycs; } # ifdef _WIN32 ret = send(rdwrfd, &(scc_ptr->out_buf[rdptr]), len, 0); # else /* ignore SIGPIPE around writes to the socket, so we */ /* can catch a closed socket and prepare to accept */ /* a new connection. Otherwise, SIGPIPE kills KEGS */ sigemptyset(&newact.sa_mask); newact.sa_handler = SIG_IGN; newact.sa_flags = 0; sigaction(SIGPIPE, &newact, &oldact); ret = send(rdwrfd, &(scc_ptr->out_buf[rdptr]), len, 0); sigaction(SIGPIPE, &oldact, 0); /* restore previous SIGPIPE behavior */ # endif /* WIN32 */ #if 0 printf("sock output: %02x\n", scc_ptr->out_buf[rdptr]); #endif } if(ret == 0) { done = 1; /* give up for now */ break; } else if(ret < 0) { /* assume socket is dead */ printf("socket write failed, resuming modem mode\n"); scc_socket_close(port, 1, dcycs); done = 1; break; } else { rdptr = rdptr + ret; if(rdptr >= SCC_OUTBUF_SIZE) { rdptr = rdptr - SCC_OUTBUF_SIZE; } scc_ptr->out_rdptr = rdptr; } } #endif } void scc_socket_modem_write(int port, int c, double dcycs) { Scc *scc_ptr; char *str; word32 modem_mode; int do_echo; int got_at; int len; scc_ptr = &(scc_stat[port]); if(scc_ptr->sockfd == -1) { scc_ptr->socket_state = 0; scc_socket_maybe_open_incoming(port, dcycs); } modem_mode = scc_ptr->modem_mode; str = &(scc_ptr->modem_cmd_str[0]); #if 0 printf("M: %02x\n", c); #endif do_echo = ((modem_mode & SCCMODEM_NOECHO) == 0); len = scc_ptr->modem_cmd_len; got_at = 0; if(len >= 2 && str[0] == 'a' && str[1] == 't') { /* we've got an 'at', do not back up past it */ got_at = 1; } if(c == 0x0d) { if(do_echo) { scc_add_to_readbuf(port, c, dcycs); /* echo cr */ scc_add_to_readbuf(port, 0x0a, dcycs); /* echo lf */ } do_echo = 0; /* already did the echo */ scc_socket_do_cmd_str(port, dcycs); scc_ptr->modem_cmd_len = 0; len = 0; str[0] = 0; } else if(c == 0x08) { if(len <= 0) { do_echo = 0; /* do not go past left margin */ } else if(len == 2 && got_at) { do_echo = 0; /* do not erase "AT" */ } else { /* erase a character */ len--; str[len] = 0; } } else if(c < 0x20) { /* ignore all control characters, don't echo */ /* includes line feeds and nulls */ do_echo = 0; } else { /* other characters */ if(len < SCC_MODEM_MAX_CMD_STR) { str[len] = tolower(c); str[len+1] = 0; len++; } } scc_ptr->modem_cmd_len = len; if(do_echo) { scc_add_to_readbuf(port, c, dcycs); /* echo */ } } void scc_socket_do_cmd_str(int port, double dcycs) { Scc *scc_ptr; char *str; int pos, len; int ret_val; int reg, reg_val; int was_amp; int c; int i; scc_ptr = &(scc_stat[port]); str = &(scc_ptr->modem_cmd_str[0]); printf("Got modem string :%s:=%02x %02x %02x\n", str, str[0], str[1], str[2]); len = scc_ptr->modem_cmd_len; str[len] = 0; str[len+1] = 0; str[len+2] = 0; pos = -1; if(len < 2) { /* just ignore it */ return; } if(str[0] != 'a' || str[1] != 't') { return; } /* Some AT command received--make sure socket 6501/6502 is open */ printf("Some AT command received, sockfd=%d\n", scc_ptr->sockfd); pos = 2 - 1; ret_val = 0; /* "OK" */ was_amp = 0; while(++pos < len) { c = str[pos] + was_amp; was_amp = 0; switch(c) { case '&': /* at& */ was_amp = 0x100; break; case 'z': /* atz */ scc_ptr->modem_mode = 0; scc_ptr->modem_s0_val = 0; pos = len; /* ignore any other commands */ break; case 'e': /* ate = echo */ c = str[pos+1]; if(c == '1') { scc_ptr->modem_mode &= ~SCCMODEM_NOECHO; pos++; } else { scc_ptr->modem_mode |= SCCMODEM_NOECHO; pos++; } break; case 'v': /* atv = verbose */ c = str[pos+1]; if(c == '1') { scc_ptr->modem_mode &= ~SCCMODEM_NOVERBOSE; pos++; } else { scc_ptr->modem_mode |= SCCMODEM_NOVERBOSE; pos++; } break; case 'o': /* ato = go online */ printf("ato\n"); if(scc_ptr->dcd && (scc_ptr->rdwrfd != -1) && (scc_ptr->socket_state == 0)) { printf("Going back online\n"); scc_ptr->socket_state = 1; scc_socket_modem_connect(port, dcycs); ret_val = -1; } break; case 'h': /* ath = hang up */ printf("ath, hanging up\n"); scc_socket_close(port, (scc_ptr->rdwrfd != -1), dcycs); /* scc_socket_maybe_open_incoming(port, dcycs); */ /* reopen listen */ break; case 'a': /* ata */ printf("Doing ATA\n"); scc_socket_do_answer(port, dcycs); ret_val = -1; break; case 'd': /* atd */ pos++; c = str[pos]; if(c == 't' || c == 'p') { /* skip tone or pulse */ pos++; } /* see if it is 111 */ if(strcmp(&str[pos], "111") == 0) { /* Do PPP! */ } else { /* get string to connect to */ /* Shift string so hostname moves to str[0] */ for(i = 0; i < len; i++) { str[i] = str[pos]; if(pos >= len) { break; } pos++; } } scc_ptr->modem_dial_or_acc_mode = 1; scc_socket_open_outgoing(port, dcycs); ret_val = -1; pos = len; /* always eat rest of the line */ break; case 's': /* atsnn=yy */ pos++; reg = 0; while(1) { c = str[pos]; if(c < '0' || c > '9') { break; } reg = (reg * 10) + c - '0'; pos++; } if(c == '?') { /* display S-register */ if(reg == 0) { scc_add_to_readbufv(port, dcycs, "S0=%d\n", scc_ptr->modem_s0_val); } break; } if(c != '=') { break; } pos++; reg_val = 0; while(1) { c = str[pos]; if(c < '0' || c >'9') { break; } reg_val = (reg_val * 10) + c - '0'; pos++; } printf("ats%d = %d\n", reg, reg_val); if(reg == 0) { scc_ptr->modem_s0_val = reg_val; } pos--; break; default: /* some command--peek into next chars to finish it */ while(1) { c = str[pos+1]; if(c >= '0' && c <= '9') { /* eat numbers */ pos++; continue; } if(c == '=') { /* eat this as well */ pos++; continue; } /* else get out */ break; } } } if(ret_val >= 0) { scc_socket_send_modem_code(port, ret_val, dcycs); } } void scc_socket_send_modem_code(int port, int code, double dcycs) { Scc *scc_ptr; char *str; word32 modem_mode; scc_ptr = &(scc_stat[port]); switch(code) { case 0: str = "OK"; break; case 1: str = "CONNECT"; break; case 2: str = "RING"; break; case 3: str = "NO CARRIER"; break; case 4: str = "ERROR"; break; case 5: str = "CONNECT 1200"; break; case 13: str = "CONNECT 9600"; break; case 16: str = "CONNECT 19200"; break; case 25: str = "CONNECT 14400"; break; case 85: str = "CONNECT 19200"; break; default: str = "ERROR"; } printf("Sending modem code %d = %s\n", code, str); modem_mode = scc_ptr->modem_mode; if(modem_mode & SCCMODEM_NOVERBOSE) { /* just the number */ scc_add_to_readbufv(port, dcycs, "%d", code); scc_add_to_readbuf(port, 0x0d, dcycs); } else { scc_add_to_readbufv(port, dcycs, "%s\n", str); } } void scc_socket_modem_hangup(int port, double dcycs) { scc_socket_send_modem_code(port, 3, dcycs); } void scc_socket_modem_connect(int port, double dcycs) { /* decide which code to send. Default to 1 if needed */ scc_socket_send_modem_code(port, 13, dcycs); /*13=9600*/ } void scc_socket_modem_do_ring(int port, double dcycs) { Scc *scc_ptr; double diff_dcycs; int num_rings; scc_ptr = &(scc_stat[port]); num_rings = scc_ptr->socket_num_rings; if(num_rings > 0 && scc_ptr->socket_state == 0) { num_rings--; diff_dcycs = dcycs - scc_ptr->socket_last_ring_dcycs; if(diff_dcycs < 2.0*1000*1000 && g_serial_modem[port]) { return; /* nothing more to do */ } printf("In modem_do_ring, ringing at %f\n", dcycs); if(g_serial_modem[port]) { scc_socket_send_modem_code(port, 2, dcycs); /* RING */ } else { num_rings = 0; } scc_ptr->socket_num_rings = num_rings; scc_ptr->socket_last_ring_dcycs = dcycs; if(num_rings <= 0) { /* decide on answering */ if(scc_ptr->modem_s0_val || (g_serial_modem[port]==0)) { scc_socket_do_answer(port, dcycs); } else { printf("No answer, closing socket\n"); scc_socket_close(port, 0, dcycs); } } } } void scc_socket_do_answer(int port, double dcycs) { Scc *scc_ptr; scc_ptr = &(scc_stat[port]); scc_ptr->modem_dial_or_acc_mode = 2; scc_accept_socket(port, dcycs); if(scc_ptr->rdwrfd == -1) { printf("Answer when rdwrfd=-1, closing\n"); scc_socket_close(port, 1, dcycs); /* send NO CARRIER message */ } else { scc_ptr->socket_state = 1; scc_socket_telnet_reqs(port, dcycs); printf("Send telnet reqs, rdwrfd=%d\n", scc_ptr->rdwrfd); if(g_serial_modem[port]) { scc_socket_modem_connect(port, dcycs); } scc_ptr->dcd = 1; /* carrier on */ scc_ptr->socket_state = 1; /* talk to socket */ scc_ptr->socket_num_rings = 0; } }