mirror of
https://github.com/kanjitalk755/macemu.git
synced 2024-07-05 09:28:57 +00:00
834 lines
19 KiB
C++
834 lines
19 KiB
C++
/*
|
|
* serial_unix.cpp - Serial device driver, Unix specific stuff
|
|
*
|
|
* Basilisk II (C) 1997-2008 Christian Bauer
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "sysdeps.h"
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/wait.h>
|
|
#include <pthread.h>
|
|
#include <semaphore.h>
|
|
#include <termios.h>
|
|
#include <errno.h>
|
|
#ifdef __linux__
|
|
#include <linux/lp.h>
|
|
#include <linux/major.h>
|
|
#include <linux/kdev_t.h>
|
|
#endif
|
|
|
|
#include "cpu_emulation.h"
|
|
#include "main.h"
|
|
#include "macos_util.h"
|
|
#include "prefs.h"
|
|
#include "serial.h"
|
|
#include "serial_defs.h"
|
|
|
|
extern "C" {
|
|
#include "sshpty.h"
|
|
}
|
|
|
|
|
|
#define DEBUG 0
|
|
#include "debug.h"
|
|
|
|
#define MONITOR 0
|
|
|
|
|
|
// IRIX missing or unsupported defines
|
|
#ifdef sgi
|
|
#ifndef CRTSCTS
|
|
#define CRTSCTS CNEW_RTSCTS
|
|
#endif
|
|
#ifndef B230400
|
|
#define B230400 B115200
|
|
#endif
|
|
#endif
|
|
|
|
|
|
// Missing functions
|
|
#ifndef HAVE_CFMAKERAW
|
|
static int cfmakeraw(struct termios *termios_p)
|
|
{
|
|
termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
|
|
termios_p->c_oflag &= ~OPOST;
|
|
termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
|
|
termios_p->c_cflag &= ~(CSIZE|PARENB);
|
|
termios_p->c_cflag |= CS8;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
|
|
// Driver private variables
|
|
class XSERDPort : public SERDPort {
|
|
public:
|
|
XSERDPort(const char *dev)
|
|
{
|
|
device_name = dev;
|
|
protocol = serial;
|
|
fd = -1;
|
|
pid = 0;
|
|
input_thread_active = output_thread_active = false;
|
|
|
|
Set_pthread_attr(&thread_attr, 2);
|
|
}
|
|
|
|
virtual ~XSERDPort()
|
|
{
|
|
if (input_thread_active) {
|
|
input_thread_cancel = true;
|
|
#ifdef HAVE_PTHREAD_CANCEL
|
|
pthread_cancel(input_thread);
|
|
#endif
|
|
pthread_join(input_thread, NULL);
|
|
sem_destroy(&input_signal);
|
|
input_thread_active = false;
|
|
}
|
|
if (output_thread_active) {
|
|
output_thread_cancel = true;
|
|
#ifdef HAVE_PTHREAD_CANCEL
|
|
pthread_cancel(output_thread);
|
|
#endif
|
|
pthread_join(output_thread, NULL);
|
|
sem_destroy(&output_signal);
|
|
output_thread_active = false;
|
|
}
|
|
}
|
|
|
|
virtual int16 open(uint16 config);
|
|
virtual int16 prime_in(uint32 pb, uint32 dce);
|
|
virtual int16 prime_out(uint32 pb, uint32 dce);
|
|
virtual int16 control(uint32 pb, uint32 dce, uint16 code);
|
|
virtual int16 status(uint32 pb, uint32 dce, uint16 code);
|
|
virtual int16 close(void);
|
|
|
|
private:
|
|
bool open_pty(void);
|
|
bool configure(uint16 config);
|
|
void set_handshake(uint32 s, bool with_dtr);
|
|
static void *input_func(void *arg);
|
|
static void *output_func(void *arg);
|
|
|
|
const char *device_name; // Device name
|
|
enum {serial, parallel, pty, midi}
|
|
protocol; // Type of device
|
|
int fd; // FD of device
|
|
pid_t pid; // PID of child process
|
|
|
|
bool io_killed; // Flag: KillIO called, I/O threads must not call deferred tasks
|
|
bool quitting; // Flag: Quit threads
|
|
|
|
pthread_attr_t thread_attr; // Input/output thread attributes
|
|
|
|
bool input_thread_active; // Flag: Input thread installed
|
|
volatile bool input_thread_cancel; // Flag: Cancel input thread
|
|
pthread_t input_thread; // Data input thread
|
|
sem_t input_signal; // Signal for input thread: execute command
|
|
uint32 input_pb; // Command parameter for input thread
|
|
|
|
bool output_thread_active; // Flag: Output thread installed
|
|
volatile bool output_thread_cancel; // Flag: Cancel output thread
|
|
pthread_t output_thread; // Data output thread
|
|
sem_t output_signal; // Signal for output thread: execute command
|
|
uint32 output_pb; // Command parameter for output thread
|
|
|
|
struct termios mode; // Terminal configuration
|
|
};
|
|
|
|
|
|
/*
|
|
* Initialization
|
|
*/
|
|
|
|
void SerialInit(void)
|
|
{
|
|
// Read serial preferences and create structs for both ports
|
|
the_serd_port[0] = new XSERDPort(PrefsFindString("seriala"));
|
|
the_serd_port[1] = new XSERDPort(PrefsFindString("serialb"));
|
|
}
|
|
|
|
|
|
/*
|
|
* Deinitialization
|
|
*/
|
|
|
|
void SerialExit(void)
|
|
{
|
|
delete (XSERDPort *)the_serd_port[0];
|
|
delete (XSERDPort *)the_serd_port[1];
|
|
}
|
|
|
|
|
|
/*
|
|
* Open serial port
|
|
*/
|
|
|
|
int16 XSERDPort::open(uint16 config)
|
|
{
|
|
// Don't open NULL name devices
|
|
if (device_name == NULL)
|
|
return openErr;
|
|
|
|
// Init variables
|
|
io_killed = false;
|
|
quitting = false;
|
|
|
|
// Open port, according to the syntax of the path
|
|
if (device_name[0] == '|') {
|
|
// Open a process via ptys
|
|
if (!open_pty())
|
|
goto open_error;
|
|
}
|
|
else if (!strcmp(device_name, "midi")) {
|
|
// MIDI: not yet implemented
|
|
return openErr;
|
|
}
|
|
else {
|
|
// Device special file
|
|
fd = ::open(device_name, O_RDWR);
|
|
if (fd < 0)
|
|
goto open_error;
|
|
|
|
#if defined(__linux__)
|
|
// Parallel port?
|
|
struct stat st;
|
|
if (fstat(fd, &st) == 0)
|
|
if (S_ISCHR(st.st_mode))
|
|
protocol = ((MAJOR(st.st_rdev) == LP_MAJOR) ? parallel : serial);
|
|
#elif defined(__FreeBSD__) || defined(__NetBSD__)
|
|
// Parallel port?
|
|
struct stat st;
|
|
if (fstat(fd, &st) == 0)
|
|
if (S_ISCHR(st.st_mode))
|
|
protocol = (((st.st_rdev >> 16) == 16) ? parallel : serial);
|
|
#endif
|
|
}
|
|
|
|
// Configure port for raw mode
|
|
if (protocol == serial || protocol == pty) {
|
|
if (tcgetattr(fd, &mode) < 0)
|
|
goto open_error;
|
|
cfmakeraw(&mode);
|
|
mode.c_cflag |= HUPCL;
|
|
mode.c_cc[VMIN] = 1;
|
|
mode.c_cc[VTIME] = 0;
|
|
tcsetattr(fd, TCSAFLUSH, &mode);
|
|
}
|
|
configure(config);
|
|
|
|
// Start input/output threads
|
|
input_thread_cancel = false;
|
|
output_thread_cancel = false;
|
|
if (sem_init(&input_signal, 0, 0) < 0)
|
|
goto open_error;
|
|
if (sem_init(&output_signal, 0, 0) < 0)
|
|
goto open_error;
|
|
input_thread_active = (pthread_create(&input_thread, &thread_attr, input_func, this) == 0);
|
|
output_thread_active = (pthread_create(&output_thread, &thread_attr, output_func, this) == 0);
|
|
if (!input_thread_active || !output_thread_active)
|
|
goto open_error;
|
|
return noErr;
|
|
|
|
open_error:
|
|
if (input_thread_active) {
|
|
input_thread_cancel = true;
|
|
#ifdef HAVE_PTHREAD_CANCEL
|
|
pthread_cancel(input_thread);
|
|
#endif
|
|
pthread_join(input_thread, NULL);
|
|
sem_destroy(&input_signal);
|
|
input_thread_active = false;
|
|
}
|
|
if (output_thread_active) {
|
|
output_thread_cancel = true;
|
|
#ifdef HAVE_PTHREAD_CANCEL
|
|
pthread_cancel(output_thread);
|
|
#endif
|
|
pthread_join(output_thread, NULL);
|
|
sem_destroy(&output_signal);
|
|
output_thread_active = false;
|
|
}
|
|
if (fd > 0) {
|
|
::close(fd);
|
|
fd = -1;
|
|
}
|
|
return openErr;
|
|
}
|
|
|
|
|
|
/*
|
|
* Read data from port
|
|
*/
|
|
|
|
int16 XSERDPort::prime_in(uint32 pb, uint32 dce)
|
|
{
|
|
// Send input command to input_thread
|
|
read_done = false;
|
|
read_pending = true;
|
|
input_pb = pb;
|
|
WriteMacInt32(input_dt + serdtDCE, dce);
|
|
sem_post(&input_signal);
|
|
return 1; // Command in progress
|
|
}
|
|
|
|
|
|
/*
|
|
* Write data to port
|
|
*/
|
|
|
|
int16 XSERDPort::prime_out(uint32 pb, uint32 dce)
|
|
{
|
|
// Send output command to output_thread
|
|
write_done = false;
|
|
write_pending = true;
|
|
output_pb = pb;
|
|
WriteMacInt32(output_dt + serdtDCE, dce);
|
|
sem_post(&output_signal);
|
|
return 1; // Command in progress
|
|
}
|
|
|
|
|
|
/*
|
|
* Control calls
|
|
*/
|
|
|
|
int16 XSERDPort::control(uint32 pb, uint32 dce, uint16 code)
|
|
{
|
|
switch (code) {
|
|
case 1: // KillIO
|
|
io_killed = true;
|
|
if (protocol == serial)
|
|
tcflush(fd, TCIOFLUSH);
|
|
while (read_pending || write_pending)
|
|
usleep(10000);
|
|
io_killed = false;
|
|
return noErr;
|
|
|
|
case kSERDConfiguration:
|
|
if (configure(ReadMacInt16(pb + csParam)))
|
|
return noErr;
|
|
else
|
|
return paramErr;
|
|
|
|
case kSERDInputBuffer:
|
|
return noErr; // Not supported under Unix
|
|
|
|
case kSERDSerHShake:
|
|
set_handshake(pb + csParam, false);
|
|
return noErr;
|
|
|
|
case kSERDSetBreak:
|
|
if (protocol == serial)
|
|
tcsendbreak(fd, 0);
|
|
return noErr;
|
|
|
|
case kSERDClearBreak:
|
|
return noErr;
|
|
|
|
case kSERDBaudRate: {
|
|
if (protocol != serial)
|
|
return noErr;
|
|
uint16 rate = ReadMacInt16(pb + csParam);
|
|
speed_t baud_rate;
|
|
if (rate <= 50) {
|
|
rate = 50; baud_rate = B50;
|
|
} else if (rate <= 75) {
|
|
rate = 75; baud_rate = B75;
|
|
} else if (rate <= 110) {
|
|
rate = 110; baud_rate = B110;
|
|
} else if (rate <= 134) {
|
|
rate = 134; baud_rate = B134;
|
|
} else if (rate <= 150) {
|
|
rate = 150; baud_rate = B150;
|
|
} else if (rate <= 200) {
|
|
rate = 200; baud_rate = B200;
|
|
} else if (rate <= 300) {
|
|
rate = 300; baud_rate = B300;
|
|
} else if (rate <= 600) {
|
|
rate = 600; baud_rate = B600;
|
|
} else if (rate <= 1200) {
|
|
rate = 1200; baud_rate = B1200;
|
|
} else if (rate <= 1800) {
|
|
rate = 1800; baud_rate = B1800;
|
|
} else if (rate <= 2400) {
|
|
rate = 2400; baud_rate = B2400;
|
|
} else if (rate <= 4800) {
|
|
rate = 4800; baud_rate = B4800;
|
|
} else if (rate <= 9600) {
|
|
rate = 9600; baud_rate = B9600;
|
|
} else if (rate <= 19200) {
|
|
rate = 19200; baud_rate = B19200;
|
|
} else if (rate <= 38400) {
|
|
rate = 38400; baud_rate = B38400;
|
|
} else if (rate <= 57600) {
|
|
rate = 57600; baud_rate = B57600;
|
|
} else {
|
|
// Just for safety in case someone wants a rate between 57600 and 65535
|
|
rate = 57600; baud_rate = B57600;
|
|
}
|
|
WriteMacInt16(pb + csParam, rate);
|
|
cfsetispeed(&mode, baud_rate);
|
|
cfsetospeed(&mode, baud_rate);
|
|
tcsetattr(fd, TCSANOW, &mode);
|
|
return noErr;
|
|
}
|
|
|
|
case kSERDHandshake:
|
|
case kSERDHandshakeRS232:
|
|
set_handshake(pb + csParam, true);
|
|
return noErr;
|
|
|
|
case kSERDMiscOptions:
|
|
if (protocol != serial)
|
|
return noErr;
|
|
if (ReadMacInt8(pb + csParam) & kOptionPreserveDTR)
|
|
mode.c_cflag &= ~HUPCL;
|
|
else
|
|
mode.c_cflag |= HUPCL;
|
|
tcsetattr(fd, TCSANOW, &mode);
|
|
return noErr;
|
|
|
|
case kSERDAssertDTR: {
|
|
if (protocol != serial)
|
|
return noErr;
|
|
unsigned int status = TIOCM_DTR;
|
|
ioctl(fd, TIOCMBIS, &status);
|
|
return noErr;
|
|
}
|
|
|
|
case kSERDNegateDTR: {
|
|
if (protocol != serial)
|
|
return noErr;
|
|
unsigned int status = TIOCM_DTR;
|
|
ioctl(fd, TIOCMBIC, &status);
|
|
return noErr;
|
|
}
|
|
|
|
case kSERDSetPEChar:
|
|
case kSERDSetPEAltChar:
|
|
return noErr; // Not supported under Unix
|
|
|
|
case kSERDResetChannel:
|
|
if (protocol == serial)
|
|
tcflush(fd, TCIOFLUSH);
|
|
return noErr;
|
|
|
|
case kSERDAssertRTS: {
|
|
if (protocol != serial)
|
|
return noErr;
|
|
unsigned int status = TIOCM_RTS;
|
|
ioctl(fd, TIOCMBIS, &status);
|
|
return noErr;
|
|
}
|
|
|
|
case kSERDNegateRTS: {
|
|
if (protocol != serial)
|
|
return noErr;
|
|
unsigned int status = TIOCM_RTS;
|
|
ioctl(fd, TIOCMBIC, &status);
|
|
return noErr;
|
|
}
|
|
|
|
case kSERD115KBaud:
|
|
if (protocol != serial)
|
|
return noErr;
|
|
cfsetispeed(&mode, B115200);
|
|
cfsetospeed(&mode, B115200);
|
|
tcsetattr(fd, TCSANOW, &mode);
|
|
return noErr;
|
|
|
|
case kSERD230KBaud:
|
|
case kSERDSetHighSpeed:
|
|
if (protocol != serial)
|
|
return noErr;
|
|
cfsetispeed(&mode, B230400);
|
|
cfsetospeed(&mode, B230400);
|
|
tcsetattr(fd, TCSANOW, &mode);
|
|
return noErr;
|
|
|
|
default:
|
|
printf("WARNING: SerialControl(): unimplemented control code %d\n", code);
|
|
return controlErr;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Status calls
|
|
*/
|
|
|
|
int16 XSERDPort::status(uint32 pb, uint32 dce, uint16 code)
|
|
{
|
|
switch (code) {
|
|
case kSERDInputCount: {
|
|
int num;
|
|
ioctl(fd, FIONREAD, &num);
|
|
WriteMacInt32(pb + csParam, num);
|
|
return noErr;
|
|
}
|
|
|
|
case kSERDStatus: {
|
|
uint32 p = pb + csParam;
|
|
WriteMacInt8(p + staCumErrs, cum_errors);
|
|
cum_errors = 0;
|
|
WriteMacInt8(p + staXOffSent, 0);
|
|
WriteMacInt8(p + staXOffHold, 0);
|
|
WriteMacInt8(p + staRdPend, read_pending);
|
|
WriteMacInt8(p + staWrPend, write_pending);
|
|
if (protocol != serial) {
|
|
WriteMacInt8(p + staCtsHold, 0);
|
|
WriteMacInt8(p + staDsrHold, 0);
|
|
WriteMacInt8(p + staModemStatus, dsrEvent | dcdEvent | ctsEvent);
|
|
} else {
|
|
unsigned int status;
|
|
ioctl(fd, TIOCMGET, &status);
|
|
WriteMacInt8(p + staCtsHold, status & TIOCM_CTS ? 0 : 1);
|
|
WriteMacInt8(p + staDsrHold, status & TIOCM_DTR ? 0 : 1);
|
|
WriteMacInt8(p + staModemStatus,
|
|
(status & TIOCM_DSR ? dsrEvent : 0)
|
|
| (status & TIOCM_RI ? riEvent : 0)
|
|
| (status & TIOCM_CD ? dcdEvent : 0)
|
|
| (status & TIOCM_CTS ? ctsEvent : 0));
|
|
}
|
|
return noErr;
|
|
}
|
|
|
|
default:
|
|
printf("WARNING: SerialStatus(): unimplemented status code %d\n", code);
|
|
return statusErr;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Close serial port
|
|
*/
|
|
|
|
int16 XSERDPort::close()
|
|
{
|
|
// Kill threads
|
|
if (input_thread_active) {
|
|
quitting = true;
|
|
sem_post(&input_signal);
|
|
pthread_join(input_thread, NULL);
|
|
input_thread_active = false;
|
|
sem_destroy(&input_signal);
|
|
}
|
|
if (output_thread_active) {
|
|
quitting = true;
|
|
sem_post(&output_signal);
|
|
pthread_join(output_thread, NULL);
|
|
output_thread_active = false;
|
|
sem_destroy(&output_signal);
|
|
}
|
|
|
|
// Close port
|
|
if (fd > 0)
|
|
::close(fd);
|
|
fd = -1;
|
|
|
|
// Wait for the subprocess to exit
|
|
if (pid)
|
|
waitpid(pid, NULL, 0);
|
|
pid = 0;
|
|
|
|
return noErr;
|
|
}
|
|
|
|
|
|
/*
|
|
* Open a process via ptys
|
|
*/
|
|
|
|
bool XSERDPort::open_pty(void)
|
|
{
|
|
// Talk to a process via a pty
|
|
char slave[128];
|
|
int slavefd;
|
|
|
|
protocol = pty;
|
|
if (!pty_allocate(&fd, &slavefd, slave, sizeof(slave)))
|
|
return false;
|
|
|
|
fflush(stdout);
|
|
fflush(stderr);
|
|
switch (pid = fork()) {
|
|
case -1: // error
|
|
return false;
|
|
break;
|
|
case 0: // child
|
|
::close(fd);
|
|
|
|
/* Make the pseudo tty our controlling tty. */
|
|
pty_make_controlling_tty(&slavefd, slave);
|
|
|
|
::close(0); dup(slavefd); // Use the slave fd for stdin,
|
|
::close(1); dup(slavefd); // stdout,
|
|
::close(2); dup(slavefd); // and stderr.
|
|
|
|
// <should we be more paranoid about closing unused fds?>
|
|
// <should we drop privileges if running setuid?>
|
|
|
|
// Let the shell do the dirty work
|
|
execlp("/bin/sh", "/bin/sh", "-c", ++device_name, (char *)NULL);
|
|
|
|
// exec failed!
|
|
printf("serial_open: could not exec %s: %s\n",
|
|
"/bin/sh", strerror(errno));
|
|
exit(1);
|
|
break;
|
|
default: // parent
|
|
// Pid was stored above
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* Configure serial port with MacOS config word
|
|
*/
|
|
|
|
bool XSERDPort::configure(uint16 config)
|
|
{
|
|
D(bug(" configure %04x\n", config));
|
|
if (protocol != serial)
|
|
return true;
|
|
|
|
// Set number of stop bits
|
|
switch (config & 0xc000) {
|
|
case stop10:
|
|
mode.c_cflag &= ~CSTOPB;
|
|
break;
|
|
case stop20:
|
|
mode.c_cflag |= CSTOPB;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
// Set parity mode
|
|
switch (config & 0x3000) {
|
|
case noParity:
|
|
mode.c_iflag &= ~INPCK;
|
|
mode.c_oflag &= ~PARENB;
|
|
break;
|
|
case oddParity:
|
|
mode.c_iflag |= INPCK;
|
|
mode.c_oflag |= PARENB;
|
|
mode.c_oflag |= PARODD;
|
|
break;
|
|
case evenParity:
|
|
mode.c_iflag |= INPCK;
|
|
mode.c_oflag |= PARENB;
|
|
mode.c_oflag &= ~PARODD;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
// Set number of data bits
|
|
switch (config & 0x0c00) {
|
|
case data5:
|
|
mode.c_cflag = mode.c_cflag & ~CSIZE | CS5;
|
|
break;
|
|
case data6:
|
|
mode.c_cflag = mode.c_cflag & ~CSIZE | CS6;
|
|
break;
|
|
case data7:
|
|
mode.c_cflag = mode.c_cflag & ~CSIZE | CS7;
|
|
break;
|
|
case data8:
|
|
mode.c_cflag = mode.c_cflag & ~CSIZE | CS8;
|
|
break;
|
|
}
|
|
|
|
// Set baud rate
|
|
speed_t baud_rate;
|
|
switch (config & 0x03ff) {
|
|
case baud150: baud_rate = B150; break;
|
|
case baud300: baud_rate = B300; break;
|
|
case baud600: baud_rate = B600; break;
|
|
case baud1200: baud_rate = B1200; break;
|
|
case baud1800: baud_rate = B1800; break;
|
|
case baud2400: baud_rate = B2400; break;
|
|
case baud4800: baud_rate = B4800; break;
|
|
case baud9600: baud_rate = B9600; break;
|
|
case baud19200: baud_rate = B19200; break;
|
|
case baud38400: baud_rate = B38400; break;
|
|
case baud57600: baud_rate = B57600; break;
|
|
default:
|
|
return false;
|
|
}
|
|
cfsetispeed(&mode, baud_rate);
|
|
cfsetospeed(&mode, baud_rate);
|
|
tcsetattr(fd, TCSANOW, &mode);
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* Set serial handshaking
|
|
*/
|
|
|
|
void XSERDPort::set_handshake(uint32 s, bool with_dtr)
|
|
{
|
|
D(bug(" set_handshake %02x %02x %02x %02x %02x %02x %02x %02x\n",
|
|
ReadMacInt8(s + 0), ReadMacInt8(s + 1), ReadMacInt8(s + 2), ReadMacInt8(s + 3),
|
|
ReadMacInt8(s + 4), ReadMacInt8(s + 5), ReadMacInt8(s + 6), ReadMacInt8(s + 7)));
|
|
if (protocol != serial)
|
|
return;
|
|
|
|
if (with_dtr) {
|
|
if (ReadMacInt8(s + shkFCTS) || ReadMacInt8(s + shkFDTR))
|
|
mode.c_cflag |= CRTSCTS;
|
|
else
|
|
mode.c_cflag &= ~CRTSCTS;
|
|
} else {
|
|
if (ReadMacInt8(s + shkFCTS))
|
|
mode.c_cflag |= CRTSCTS;
|
|
else
|
|
mode.c_cflag &= ~CRTSCTS;
|
|
}
|
|
|
|
D(bug(" %sware flow control\n", mode.c_cflag & CRTSCTS ? "hard" : "soft"));
|
|
tcsetattr(fd, TCSANOW, &mode);
|
|
}
|
|
|
|
|
|
/*
|
|
* Data input thread
|
|
*/
|
|
|
|
void *XSERDPort::input_func(void *arg)
|
|
{
|
|
XSERDPort *s = (XSERDPort *)arg;
|
|
while (!s->input_thread_cancel) {
|
|
|
|
// Wait for commands
|
|
sem_wait(&s->input_signal);
|
|
if (s->quitting)
|
|
break;
|
|
|
|
// Execute command
|
|
void *buf = Mac2HostAddr(ReadMacInt32(s->input_pb + ioBuffer));
|
|
uint32 length = ReadMacInt32(s->input_pb + ioReqCount);
|
|
D(bug("input_func waiting for %ld bytes of data...\n", length));
|
|
int32 actual = read(s->fd, buf, length);
|
|
D(bug(" %ld bytes received\n", actual));
|
|
|
|
#if MONITOR
|
|
bug("Receiving serial data:\n");
|
|
uint8 *adr = (uint8 *)buf;
|
|
for (int i=0; i<actual; i++) {
|
|
bug("%02x ", adr[i]);
|
|
}
|
|
bug("\n");
|
|
#endif
|
|
|
|
// KillIO called? Then simply return
|
|
if (s->io_killed) {
|
|
|
|
WriteMacInt16(s->input_pb + ioResult, uint16(abortErr));
|
|
WriteMacInt32(s->input_pb + ioActCount, 0);
|
|
s->read_pending = s->read_done = false;
|
|
|
|
} else {
|
|
|
|
// Set error code
|
|
if (actual >= 0) {
|
|
WriteMacInt32(s->input_pb + ioActCount, actual);
|
|
WriteMacInt32(s->input_dt + serdtResult, noErr);
|
|
} else {
|
|
WriteMacInt32(s->input_pb + ioActCount, 0);
|
|
WriteMacInt32(s->input_dt + serdtResult, uint16(readErr));
|
|
}
|
|
|
|
// Trigger serial interrupt
|
|
D(bug(" triggering serial interrupt\n"));
|
|
s->read_done = true;
|
|
SetInterruptFlag(INTFLAG_SERIAL);
|
|
TriggerInterrupt();
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Data output thread
|
|
*/
|
|
|
|
void *XSERDPort::output_func(void *arg)
|
|
{
|
|
XSERDPort *s = (XSERDPort *)arg;
|
|
while (!s->output_thread_cancel) {
|
|
|
|
// Wait for commands
|
|
sem_wait(&s->output_signal);
|
|
if (s->quitting)
|
|
break;
|
|
|
|
// Execute command
|
|
void *buf = Mac2HostAddr(ReadMacInt32(s->output_pb + ioBuffer));
|
|
uint32 length = ReadMacInt32(s->output_pb + ioReqCount);
|
|
D(bug("output_func transmitting %ld bytes of data...\n", length));
|
|
|
|
#if MONITOR
|
|
bug("Sending serial data:\n");
|
|
uint8 *adr = (uint8 *)buf;
|
|
for (int i=0; i<length; i++) {
|
|
bug("%02x ", adr[i]);
|
|
}
|
|
bug("\n");
|
|
#endif
|
|
|
|
int32 actual = write(s->fd, buf, length);
|
|
D(bug(" %ld bytes transmitted\n", actual));
|
|
|
|
// KillIO called? Then simply return
|
|
if (s->io_killed) {
|
|
|
|
WriteMacInt16(s->output_pb + ioResult, uint16(abortErr));
|
|
WriteMacInt32(s->output_pb + ioActCount, 0);
|
|
s->write_pending = s->write_done = false;
|
|
|
|
} else {
|
|
|
|
// Set error code
|
|
if (actual >= 0) {
|
|
WriteMacInt32(s->output_pb + ioActCount, actual);
|
|
WriteMacInt32(s->output_dt + serdtResult, noErr);
|
|
} else {
|
|
WriteMacInt32(s->output_pb + ioActCount, 0);
|
|
WriteMacInt32(s->output_dt + serdtResult, uint16(writErr));
|
|
}
|
|
|
|
// Trigger serial interrupt
|
|
D(bug(" triggering serial interrupt\n"));
|
|
s->write_done = true;
|
|
SetInterruptFlag(INTFLAG_SERIAL);
|
|
TriggerInterrupt();
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|