mirror of https://github.com/aufflick/kegs.git
256 lines
5.0 KiB
C
256 lines
5.0 KiB
C
/************************************************************************/
|
|
/* KEGS: Apple //gs Emulator */
|
|
/* Copyright 2002 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.4 2003-11-20 23:43:41-05 kentd Exp $";
|
|
|
|
/* This file contains the Unix socket calls */
|
|
|
|
#include "defc.h"
|
|
#include "scc.h"
|
|
#include <signal.h>
|
|
|
|
extern Scc scc_stat[2];
|
|
|
|
void
|
|
scc_socket_init(int port)
|
|
{
|
|
#ifdef SCC_SOCKETS
|
|
Scc *scc_ptr;
|
|
struct sockaddr_in sa_in;
|
|
int on;
|
|
int flags;
|
|
int ret;
|
|
int sockfd;
|
|
int inc;
|
|
|
|
inc = 0;
|
|
|
|
scc_ptr = &(scc_stat[port]);
|
|
|
|
scc_ptr->state = -1; /* mark as failed for now */
|
|
scc_ptr->host_aux1 = sizeof(struct sockaddr_in);
|
|
scc_ptr->host_handle = malloc(scc_ptr->host_aux1);
|
|
memset(scc_ptr->host_handle, 0, scc_ptr->host_aux1);
|
|
|
|
while(1) {
|
|
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
if(sockfd < 0) {
|
|
printf("socket ret: %d, errno: %d\n", sockfd, errno);
|
|
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);
|
|
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) {
|
|
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");
|
|
return;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
printf("SCC port %d is at unix port %d\n", port, 6501 + port + inc);
|
|
|
|
ret = listen(sockfd, 1);
|
|
|
|
flags = fcntl(sockfd, F_GETFL, 0);
|
|
if(flags == -1) {
|
|
printf("fcntl GETFL ret: %d, errno: %d\n", flags, errno);
|
|
return;
|
|
}
|
|
ret = fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
|
|
if(ret == -1) {
|
|
printf("fcntl SETFL ret: %d, errno: %d\n", ret, errno);
|
|
return;
|
|
}
|
|
|
|
scc_ptr->accfd = sockfd;
|
|
scc_ptr->state = 1; /* successful socket */
|
|
#endif /* SCC_SOCKETS */
|
|
}
|
|
|
|
void
|
|
scc_socket_change_params(int port)
|
|
{
|
|
#ifdef SCC_SOCKETS
|
|
#endif
|
|
}
|
|
|
|
void
|
|
scc_accept_socket(int port)
|
|
{
|
|
#ifdef SCC_SOCKETS
|
|
Scc *scc_ptr;
|
|
int flags;
|
|
int rdwrfd;
|
|
int ret;
|
|
|
|
scc_ptr = &(scc_stat[port]);
|
|
|
|
if(scc_ptr->rdwrfd <= 0) {
|
|
rdwrfd = accept(scc_ptr->accfd, scc_ptr->host_handle,
|
|
&(scc_ptr->host_aux1));
|
|
if(rdwrfd < 0) {
|
|
return;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
scc_ptr->rdwrfd = rdwrfd;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
scc_socket_fill_readbuf(int port, double dcycs)
|
|
{
|
|
#ifdef SCC_SOCKETS
|
|
byte tmp_buf[256];
|
|
Scc *scc_ptr;
|
|
int rdwrfd;
|
|
int i;
|
|
int ret;
|
|
|
|
scc_ptr = &(scc_stat[port]);
|
|
|
|
/* Accept socket if not already open */
|
|
scc_accept_socket(port);
|
|
|
|
rdwrfd = scc_ptr->rdwrfd;
|
|
if(rdwrfd < 0) {
|
|
return;
|
|
}
|
|
|
|
/* Try reading some bytes */
|
|
ret = read(rdwrfd, tmp_buf, 256);
|
|
if(ret > 0) {
|
|
for(i = 0; i < ret; i++) {
|
|
if(tmp_buf[i] == 0) {
|
|
/* Skip null chars */
|
|
continue;
|
|
}
|
|
scc_add_to_readbuf(port, tmp_buf[i], dcycs);
|
|
}
|
|
} else if(ret == 0) {
|
|
/* assume socket close */
|
|
close(rdwrfd);
|
|
scc_ptr->rdwrfd = -1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
scc_socket_empty_writebuf(int port)
|
|
{
|
|
#ifdef SCC_SOCKETS
|
|
struct sigaction newact, oldact;
|
|
Scc *scc_ptr;
|
|
int rdptr;
|
|
int wrptr;
|
|
int rdwrfd;
|
|
int done;
|
|
int ret;
|
|
int len;
|
|
|
|
scc_ptr = &(scc_stat[port]);
|
|
|
|
scc_accept_socket(port);
|
|
|
|
rdwrfd = scc_ptr->rdwrfd;
|
|
if(rdwrfd < 0) {
|
|
return;
|
|
}
|
|
|
|
/* Try writing some bytes */
|
|
done = 0;
|
|
while(!done) {
|
|
rdptr = scc_ptr->out_rdptr;
|
|
wrptr = scc_ptr->out_wrptr;
|
|
if(rdptr == wrptr) {
|
|
done = 1;
|
|
break;
|
|
}
|
|
len = wrptr - rdptr;
|
|
if(len < 0) {
|
|
len = SCC_OUTBUF_SIZE - rdptr;
|
|
}
|
|
if(len > 32) {
|
|
len = 32;
|
|
}
|
|
if(len <= 0) {
|
|
done = 1;
|
|
break;
|
|
}
|
|
|
|
/* ignore SIGPIPE around writes to the socket, so we can */
|
|
/* catch a closed socket and prepare to re-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 = write(rdwrfd, &(scc_ptr->out_buf[rdptr]), len);
|
|
|
|
sigaction(SIGPIPE, &oldact, 0);
|
|
/* restore previous SIGPIPE behavior */
|
|
|
|
if(ret == 0) {
|
|
done = 1; /* give up for now */
|
|
break;
|
|
} else if(ret < 0) {
|
|
/* assume socket is dead */
|
|
close(rdwrfd);
|
|
scc_ptr->rdwrfd = -1;
|
|
done = 1;
|
|
break;
|
|
} else {
|
|
rdptr = rdptr + ret;
|
|
if(rdptr >= SCC_OUTBUF_SIZE) {
|
|
rdptr = rdptr - SCC_OUTBUF_SIZE;
|
|
}
|
|
scc_ptr->out_rdptr = rdptr;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|