kegs/src/scc_socket_driver.c

1133 lines
26 KiB
C

/************************************************************************/
/* 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 <signal.h>
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;
}
}