mirror of
https://github.com/kanjitalk755/macemu.git
synced 2024-06-18 12:29:39 +00:00
862 lines
23 KiB
C++
862 lines
23 KiB
C++
/*
|
|
* serial_amiga.cpp - Serial device driver, AmigaOS 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 <exec/types.h>
|
|
#include <exec/memory.h>
|
|
#include <exec/errors.h>
|
|
#include <dos/dos.h>
|
|
#include <dos/dosextens.h>
|
|
#include <dos/dostags.h>
|
|
#include <devices/serial.h>
|
|
#include <devices/parallel.h>
|
|
#define __USE_SYSBASE
|
|
#include <proto/exec.h>
|
|
#include <proto/dos.h>
|
|
#include <inline/exec.h>
|
|
#include <inline/dos.h>
|
|
|
|
#include "sysdeps.h"
|
|
#include "cpu_emulation.h"
|
|
#include "main.h"
|
|
#include "macos_util.h"
|
|
#include "prefs.h"
|
|
#include "serial.h"
|
|
#include "serial_defs.h"
|
|
|
|
#define DEBUG 0
|
|
#include "debug.h"
|
|
|
|
#define MONITOR 0
|
|
|
|
|
|
// These messages are sent to the serial process
|
|
const uint32 MSG_QUERY = 'qery'; // Query port status, return status in control_io
|
|
const uint32 MSG_SET_PARAMS = 'setp'; // Set serial parameters (parameters in control_io)
|
|
const uint32 MSG_SET_PAR_PARAMS = 'pstp'; // Set parallel parameters (parameters in control_io)
|
|
const uint32 MSG_KILL_IO = 'kill'; // Kill pending I/O requests
|
|
const uint32 MSG_BREAK = 'brek'; // Send break
|
|
const uint32 MSG_RESET = 'rset'; // Reset channel
|
|
const uint32 MSG_PRIME_IN = 'prin'; // Data input
|
|
const uint32 MSG_PRIME_OUT = 'pout'; // Data output
|
|
|
|
struct SerMessage : public Message {
|
|
SerMessage(uint32 what_, const struct MsgPort *reply_port = NULL)
|
|
{
|
|
what = what_;
|
|
mn_ReplyPort = (struct MsgPort *)reply_port;
|
|
mn_Length = sizeof(*this);
|
|
}
|
|
uint32 what;
|
|
uint32 pb;
|
|
};
|
|
|
|
|
|
// Driver private variables
|
|
class ASERDPort : public SERDPort {
|
|
public:
|
|
ASERDPort(const char *dev)
|
|
{
|
|
device_name = dev;
|
|
if (dev && dev[0] == '*') {
|
|
is_parallel = true;
|
|
device_name++;
|
|
} else
|
|
is_parallel = false;
|
|
control_io = NULL;
|
|
serial_proc = NULL;
|
|
reply_port = NULL;
|
|
}
|
|
|
|
virtual ~ASERDPort()
|
|
{
|
|
}
|
|
|
|
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 configure(uint16 config);
|
|
void set_handshake(uint32 s, bool with_dtr);
|
|
void send_to_proc(uint32 what, uint32 pb = 0);
|
|
bool query(void);
|
|
bool set_params(void);
|
|
bool set_par_params(void);
|
|
void conv_error(struct IOExtSer *io, uint32 dt);
|
|
static void serial_func(void);
|
|
|
|
const char *device_name; // Device name
|
|
bool is_parallel; // Flag: Port is parallel
|
|
IOExtSer *control_io; // IORequest for setting serial port characteristics etc.
|
|
|
|
struct Process *serial_proc; // Serial device handler process
|
|
bool proc_error; // Flag: process didn't initialize
|
|
struct MsgPort *proc_port; // Message port of process, for communication with main task
|
|
struct MsgPort *reply_port; // Reply port for communication with process
|
|
|
|
uint8 err_mask; // shkErrs
|
|
};
|
|
|
|
|
|
// Global variables
|
|
static void *proc_arg; // Argument to process
|
|
extern struct Task *MainTask; // Pointer to main task (from main_amiga.cpp)
|
|
|
|
|
|
/*
|
|
* Initialization
|
|
*/
|
|
|
|
void SerialInit(void)
|
|
{
|
|
// Read serial preferences and create structs for both ports
|
|
the_serd_port[0] = new ASERDPort(PrefsFindString("seriala"));
|
|
the_serd_port[1] = new ASERDPort(PrefsFindString("serialb"));
|
|
}
|
|
|
|
|
|
/*
|
|
* Deinitialization
|
|
*/
|
|
|
|
void SerialExit(void)
|
|
{
|
|
delete (ASERDPort *)the_serd_port[0];
|
|
delete (ASERDPort *)the_serd_port[1];
|
|
}
|
|
|
|
|
|
/*
|
|
* Open serial port
|
|
*/
|
|
|
|
int16 ASERDPort::open(uint16 config)
|
|
{
|
|
// Don't open NULL name devices
|
|
if (device_name == NULL)
|
|
return openErr;
|
|
|
|
// Init variables
|
|
err_mask = 0;
|
|
|
|
// Create message port
|
|
reply_port = CreateMsgPort();
|
|
if (reply_port == NULL)
|
|
goto open_error;
|
|
|
|
// Start process
|
|
proc_error = false;
|
|
proc_arg = this;
|
|
SetSignal(0, SIGF_SINGLE);
|
|
serial_proc = CreateNewProcTags(
|
|
NP_Entry, (ULONG)serial_func,
|
|
NP_Name, (ULONG)"Basilisk II Serial Task",
|
|
NP_Priority, 1,
|
|
TAG_END
|
|
);
|
|
if (serial_proc == NULL)
|
|
goto open_error;
|
|
|
|
// Wait for signal from process
|
|
Wait(SIGF_SINGLE);
|
|
|
|
// Initialization error? Then bail out
|
|
if (proc_error)
|
|
goto open_error;
|
|
|
|
// Configure port
|
|
configure(config);
|
|
return noErr;
|
|
|
|
open_error:
|
|
serial_proc = NULL;
|
|
if (reply_port) {
|
|
DeleteMsgPort(reply_port);
|
|
reply_port = NULL;
|
|
}
|
|
return openErr;
|
|
}
|
|
|
|
|
|
/*
|
|
* Read data from port
|
|
*/
|
|
|
|
int16 ASERDPort::prime_in(uint32 pb, uint32 dce)
|
|
{
|
|
// Send input command to serial process
|
|
D(bug("primein\n"));
|
|
read_done = false;
|
|
read_pending = true;
|
|
WriteMacInt32(input_dt + serdtDCE, dce);
|
|
send_to_proc(MSG_PRIME_IN, pb);
|
|
return 1; // Command in progress
|
|
}
|
|
|
|
|
|
/*
|
|
* Write data to port
|
|
*/
|
|
|
|
int16 ASERDPort::prime_out(uint32 pb, uint32 dce)
|
|
{
|
|
// Send output command to serial process
|
|
D(bug("primeout\n"));
|
|
write_done = false;
|
|
write_pending = true;
|
|
WriteMacInt32(output_dt + serdtDCE, dce);
|
|
send_to_proc(MSG_PRIME_OUT, pb);
|
|
return 1; // Command in progress
|
|
}
|
|
|
|
|
|
/*
|
|
* Control calls
|
|
*/
|
|
|
|
int16 ASERDPort::control(uint32 pb, uint32 dce, uint16 code)
|
|
{
|
|
D(bug("control(%ld)\n", (uint32)code));
|
|
switch (code) {
|
|
case 1: // KillIO
|
|
send_to_proc(MSG_KILL_IO);
|
|
return noErr;
|
|
|
|
case kSERDConfiguration:
|
|
if (configure(ReadMacInt16(pb + csParam)))
|
|
return noErr;
|
|
else
|
|
return paramErr;
|
|
|
|
case kSERDInputBuffer: {
|
|
if (is_parallel)
|
|
return noErr;
|
|
int buf = ReadMacInt16(pb + csParam + 4) & 0xffffffc0;
|
|
if (buf < 1024) // 1k minimum
|
|
buf = 1024;
|
|
D(bug(" buffer size is now %08lx\n", buf));
|
|
control_io->io_RBufLen = buf;
|
|
return set_params() ? noErr : paramErr;
|
|
}
|
|
|
|
case kSERDSerHShake:
|
|
set_handshake(pb + csParam, false);
|
|
return noErr;
|
|
|
|
case kSERDSetBreak:
|
|
if (!is_parallel)
|
|
send_to_proc(MSG_BREAK);
|
|
return noErr;
|
|
|
|
case kSERDClearBreak:
|
|
return noErr;
|
|
|
|
case kSERDBaudRate:
|
|
if (is_parallel)
|
|
return noErr;
|
|
control_io->io_Baud = ReadMacInt16(pb + csParam);
|
|
D(bug(" baud rate %ld\n", control_io->io_Baud));
|
|
return set_params() ? noErr : paramErr;
|
|
|
|
case kSERDHandshake:
|
|
case kSERDHandshakeRS232:
|
|
set_handshake(pb + csParam, true);
|
|
return noErr;
|
|
|
|
case kSERDClockMIDI:
|
|
if (is_parallel)
|
|
return noErr;
|
|
control_io->io_Baud = 31250;
|
|
control_io->io_SerFlags = SERF_XDISABLED | SERF_SHARED;
|
|
control_io->io_StopBits = 1;
|
|
control_io->io_ReadLen = control_io->io_WriteLen = 8;
|
|
return set_params() ? noErr : paramErr;
|
|
|
|
case kSERDMiscOptions:
|
|
case kSERDAssertDTR:
|
|
case kSERDNegateDTR:
|
|
case kSERDSetPEChar:
|
|
case kSERDSetPEAltChar:
|
|
case kSERDAssertRTS:
|
|
case kSERDNegateRTS:
|
|
return noErr; // Not supported under AmigaOS
|
|
|
|
case kSERD115KBaud:
|
|
if (is_parallel)
|
|
return noErr;
|
|
control_io->io_Baud = 115200;
|
|
return set_params() ? noErr : paramErr;
|
|
|
|
case kSERD230KBaud:
|
|
case kSERDSetHighSpeed:
|
|
if (is_parallel)
|
|
return noErr;
|
|
control_io->io_Baud = 230400;
|
|
return set_params() ? noErr : paramErr;
|
|
|
|
case kSERDResetChannel:
|
|
send_to_proc(MSG_RESET);
|
|
return noErr;
|
|
|
|
default:
|
|
printf("WARNING: SerialControl(): unimplemented control code %d\n", code);
|
|
return controlErr;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Status calls
|
|
*/
|
|
|
|
int16 ASERDPort::status(uint32 pb, uint32 dce, uint16 code)
|
|
{
|
|
D(bug("status(%ld)\n", (uint32)code));
|
|
switch (code) {
|
|
case kSERDInputCount:
|
|
WriteMacInt32(pb + csParam, 0);
|
|
if (!is_parallel) {
|
|
if (!query())
|
|
return noErr;
|
|
D(bug("status(2) successful, returning %08lx\n", control_io->IOSer.io_Actual));
|
|
WriteMacInt32(pb + csParam, control_io->IOSer.io_Actual);
|
|
}
|
|
return noErr;
|
|
|
|
case kSERDStatus: {
|
|
uint32 p = pb + csParam;
|
|
WriteMacInt8(p + staCumErrs, cum_errors);
|
|
cum_errors = 0;
|
|
WriteMacInt8(p + staRdPend, read_pending);
|
|
WriteMacInt8(p + staWrPend, write_pending);
|
|
if (is_parallel) {
|
|
WriteMacInt8(p + staXOffSent, 0);
|
|
WriteMacInt8(p + staXOffHold, 0);
|
|
WriteMacInt8(p + staCtsHold, 0);
|
|
WriteMacInt8(p + staDsrHold, 0);
|
|
WriteMacInt8(p + staModemStatus, dsrEvent | dcdEvent | ctsEvent);
|
|
} else {
|
|
query();
|
|
WriteMacInt8(p + staXOffSent,
|
|
(control_io->io_Status & IO_STATF_XOFFREAD ? xOffWasSent : 0)
|
|
| (control_io->io_Status & (1 << 6) ? dtrNegated : 0)); // RTS
|
|
WriteMacInt8(p + staXOffHold, control_io->io_Status & IO_STATF_XOFFWRITE);
|
|
WriteMacInt8(p + staCtsHold, control_io->io_Status & (1 << 4)); // CTS
|
|
WriteMacInt8(p + staDsrHold, control_io->io_Status & (1 << 3)); // DSR
|
|
WriteMacInt8(p + staModemStatus,
|
|
(control_io->io_Status & (1 << 3) ? 0 : dsrEvent)
|
|
| (control_io->io_Status & (1 << 2) ? riEvent : 0)
|
|
| (control_io->io_Status & (1 << 5) ? 0 : dcdEvent)
|
|
| (control_io->io_Status & (1 << 4) ? 0 : ctsEvent)
|
|
| (control_io->io_Status & IO_STATF_READBREAK ? breakEvent : 0));
|
|
}
|
|
return noErr;
|
|
}
|
|
|
|
default:
|
|
printf("WARNING: SerialStatus(): unimplemented status code %d\n", code);
|
|
return statusErr;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Close serial port
|
|
*/
|
|
|
|
int16 ASERDPort::close()
|
|
{
|
|
// Stop process
|
|
if (serial_proc) {
|
|
SetSignal(0, SIGF_SINGLE);
|
|
Signal(&serial_proc->pr_Task, SIGBREAKF_CTRL_C);
|
|
Wait(SIGF_SINGLE);
|
|
}
|
|
|
|
// Delete reply port
|
|
if (reply_port) {
|
|
DeleteMsgPort(reply_port);
|
|
reply_port = NULL;
|
|
}
|
|
return noErr;
|
|
}
|
|
|
|
|
|
/*
|
|
* Configure serial port with MacOS config word
|
|
*/
|
|
|
|
bool ASERDPort::configure(uint16 config)
|
|
{
|
|
D(bug(" configure %04lx\n", (uint32)config));
|
|
if (is_parallel)
|
|
return true;
|
|
|
|
// Set number of stop bits
|
|
switch (config & 0xc000) {
|
|
case stop10:
|
|
control_io->io_StopBits = 1;
|
|
break;
|
|
case stop20:
|
|
control_io->io_StopBits = 2;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
// Set parity mode
|
|
switch (config & 0x3000) {
|
|
case noParity:
|
|
control_io->io_SerFlags &= ~SERF_PARTY_ON;
|
|
break;
|
|
case oddParity:
|
|
control_io->io_SerFlags |= SERF_PARTY_ON | SERF_PARTY_ODD;
|
|
break;
|
|
case evenParity:
|
|
control_io->io_SerFlags |= SERF_PARTY_ON;
|
|
control_io->io_SerFlags &= ~SERF_PARTY_ODD;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
// Set number of data bits
|
|
switch (config & 0x0c00) {
|
|
case data5:
|
|
control_io->io_ReadLen = control_io->io_WriteLen = 5;
|
|
break;
|
|
case data6:
|
|
control_io->io_ReadLen = control_io->io_WriteLen = 6;
|
|
break;
|
|
case data7:
|
|
control_io->io_ReadLen = control_io->io_WriteLen = 7;
|
|
break;
|
|
case data8:
|
|
control_io->io_ReadLen = control_io->io_WriteLen = 8;
|
|
break;
|
|
}
|
|
|
|
// Set baud rate
|
|
control_io->io_Baud = 115200 / ((config & 0x03ff) + 2);
|
|
return set_params();
|
|
}
|
|
|
|
|
|
/*
|
|
* Set serial handshaking
|
|
*/
|
|
|
|
void ASERDPort::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)));
|
|
|
|
err_mask = ReadMacInt8(s + shkErrs);
|
|
|
|
if (is_parallel) {
|
|
|
|
// Parallel handshake
|
|
if (with_dtr) {
|
|
if (ReadMacInt8(s + shkFCTS) || ReadMacInt8(s + shkFDTR))
|
|
((IOExtPar *)control_io)->io_ParFlags |= PARF_ACKMODE;
|
|
else
|
|
((IOExtPar *)control_io)->io_ParFlags &= ~PARF_ACKMODE;
|
|
} else {
|
|
if (ReadMacInt8(s + shkFCTS))
|
|
((IOExtPar *)control_io)->io_ParFlags |= PARF_ACKMODE;
|
|
else
|
|
((IOExtPar *)control_io)->io_ParFlags &= ~PARF_ACKMODE;
|
|
}
|
|
set_par_params();
|
|
|
|
} else {
|
|
|
|
// Serial handshake
|
|
if (ReadMacInt8(s + shkFXOn) || ReadMacInt8(s + shkFInX))
|
|
control_io->io_SerFlags &= ~SERF_XDISABLED;
|
|
else
|
|
control_io->io_SerFlags |= SERF_XDISABLED;
|
|
|
|
if (with_dtr) {
|
|
if (ReadMacInt8(s + shkFCTS) || ReadMacInt8(s + shkFDTR))
|
|
control_io->io_SerFlags |= SERF_7WIRE;
|
|
else
|
|
control_io->io_SerFlags &= ~SERF_7WIRE;
|
|
} else {
|
|
if (ReadMacInt8(s + shkFCTS))
|
|
control_io->io_SerFlags |= SERF_7WIRE;
|
|
else
|
|
control_io->io_SerFlags &= ~SERF_7WIRE;
|
|
}
|
|
control_io->io_CtlChar = ReadMacInt16(s + shkXOn) << 16;
|
|
set_params();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Send message to serial process
|
|
*/
|
|
|
|
void ASERDPort::send_to_proc(uint32 what, uint32 pb)
|
|
{
|
|
D(bug("sending %08lx to serial_proc\n", what));
|
|
SerMessage msg(what, reply_port);
|
|
msg.pb = pb;
|
|
PutMsg(proc_port, &msg);
|
|
WaitPort(reply_port);
|
|
GetMsg(reply_port);
|
|
D(bug(" sent\n"));
|
|
}
|
|
|
|
|
|
/*
|
|
* Query serial port status
|
|
*/
|
|
|
|
bool ASERDPort::query(void)
|
|
{
|
|
send_to_proc(MSG_QUERY);
|
|
return control_io->IOSer.io_Error == 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Set serial parameters
|
|
*/
|
|
|
|
bool ASERDPort::set_params(void)
|
|
{
|
|
// Set/clear RadBoogie
|
|
UBYTE flags = control_io->io_SerFlags;
|
|
if (!(flags & SERF_PARTY_ON) && (flags & SERF_XDISABLED) && control_io->io_ReadLen == 8)
|
|
control_io->io_SerFlags |= SERF_RAD_BOOGIE;
|
|
else
|
|
control_io->io_SerFlags &= ~SERF_RAD_BOOGIE;
|
|
|
|
// Send message to serial process
|
|
send_to_proc(MSG_SET_PARAMS);
|
|
return control_io->IOSer.io_Error == 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Set parallel parameters
|
|
*/
|
|
|
|
bool ASERDPort::set_par_params(void)
|
|
{
|
|
send_to_proc(MSG_SET_PAR_PARAMS);
|
|
return control_io->IOSer.io_Error == 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Convert AmigaOS error code to MacOS error code, set serdtResult and cum_errors
|
|
*/
|
|
|
|
void ASERDPort::conv_error(struct IOExtSer *io, uint32 dt)
|
|
{
|
|
int16 oserr;
|
|
uint8 cum;
|
|
|
|
BYTE err = io->IOSer.io_Error;
|
|
if (err == 0 || err == IOERR_NOCMD) {
|
|
oserr = 0;
|
|
cum = 0;
|
|
} else {
|
|
if (is_parallel) {
|
|
oserr = (err_mask & framingErr) ? rcvrErr : 0;
|
|
cum = framingErr;
|
|
} else {
|
|
switch (io->IOSer.io_Error) {
|
|
case SerErr_DetectedBreak:
|
|
oserr = breakRecd;
|
|
cum = breakErr;
|
|
break;
|
|
case SerErr_ParityErr:
|
|
oserr = (err_mask & parityErr) ? rcvrErr : 0;
|
|
cum = parityErr;
|
|
break;
|
|
case SerErr_BufOverflow:
|
|
oserr = (err_mask & swOverrunErr) ? rcvrErr : 0;
|
|
cum = swOverrunErr;
|
|
break;
|
|
case SerErr_LineErr:
|
|
oserr = (err_mask & hwOverrunErr) ? rcvrErr : 0;
|
|
cum = hwOverrunErr;
|
|
break;
|
|
default:
|
|
oserr = (err_mask & framingErr) ? rcvrErr : 0;
|
|
cum = framingErr;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
WriteMacInt32(dt + serdtResult, oserr);
|
|
cum_errors |= cum;
|
|
}
|
|
|
|
|
|
/*
|
|
* Process for communication with the serial.device
|
|
*/
|
|
|
|
__saveds void ASERDPort::serial_func(void)
|
|
{
|
|
struct ASERDPort *obj = (ASERDPort *)proc_arg;
|
|
struct MsgPort *proc_port = NULL, *io_port = NULL, *control_port = NULL;
|
|
struct IOExtSer *read_io = NULL, *write_io = NULL, *control_io = NULL;
|
|
uint8 orig_params[sizeof(struct IOExtSer)];
|
|
bool opened = false;
|
|
ULONG io_mask = 0, proc_port_mask = 0;
|
|
|
|
// Default: error occured
|
|
obj->proc_error = true;
|
|
|
|
// Create message port for communication with main task
|
|
proc_port = CreateMsgPort();
|
|
if (proc_port == NULL)
|
|
goto quit;
|
|
proc_port_mask = 1 << proc_port->mp_SigBit;
|
|
|
|
// Create message ports for serial.device I/O
|
|
io_port = CreateMsgPort();
|
|
if (io_port == NULL)
|
|
goto quit;
|
|
io_mask = 1 << io_port->mp_SigBit;
|
|
control_port = CreateMsgPort();
|
|
if (control_port == NULL)
|
|
goto quit;
|
|
|
|
// Create IORequests
|
|
read_io = (struct IOExtSer *)CreateIORequest(io_port, sizeof(struct IOExtSer));
|
|
write_io = (struct IOExtSer *)CreateIORequest(io_port, sizeof(struct IOExtSer));
|
|
control_io = (struct IOExtSer *)CreateIORequest(control_port, sizeof(struct IOExtSer));
|
|
if (read_io == NULL || write_io == NULL || control_io == NULL)
|
|
goto quit;
|
|
read_io->IOSer.io_Message.mn_Node.ln_Type = 0; // Avoid CheckIO() bug
|
|
write_io->IOSer.io_Message.mn_Node.ln_Type = 0;
|
|
control_io->IOSer.io_Message.mn_Node.ln_Type = 0;
|
|
|
|
// Parse device name
|
|
char dev_name[256];
|
|
ULONG dev_unit;
|
|
if (sscanf(obj->device_name, "%[^/]/%ld", dev_name, &dev_unit) < 2)
|
|
goto quit;
|
|
|
|
// Open device
|
|
if (obj->is_parallel)
|
|
((IOExtPar *)read_io)->io_ParFlags = PARF_SHARED;
|
|
else
|
|
read_io->io_SerFlags = SERF_SHARED | SERF_7WIRE;
|
|
if (OpenDevice((UBYTE *) dev_name, dev_unit, (struct IORequest *)read_io, 0) || read_io->IOSer.io_Device == NULL)
|
|
goto quit;
|
|
opened = true;
|
|
|
|
// Copy IORequests
|
|
memcpy(write_io, read_io, sizeof(struct IOExtSer));
|
|
memcpy(control_io, read_io, sizeof(struct IOExtSer));
|
|
|
|
// Attach control_io to control_port and set default values
|
|
control_io->IOSer.io_Message.mn_ReplyPort = control_port;
|
|
if (!obj->is_parallel) {
|
|
control_io->io_CtlChar = SER_DEFAULT_CTLCHAR;
|
|
control_io->io_RBufLen = 64;
|
|
control_io->io_ExtFlags = 0;
|
|
control_io->io_Baud = 9600;
|
|
control_io->io_BrkTime = 250000;
|
|
control_io->io_ReadLen = control_io->io_WriteLen = 8;
|
|
control_io->io_StopBits = 1;
|
|
control_io->io_SerFlags = SERF_SHARED;
|
|
control_io->IOSer.io_Command = SDCMD_SETPARAMS;
|
|
DoIO((struct IORequest *)control_io);
|
|
memcpy(orig_params, &(control_io->io_CtlChar), (uint8 *)&(control_io->io_Status) - (uint8 *)&(control_io->io_CtlChar));
|
|
}
|
|
|
|
// Initialization went well, inform main task
|
|
obj->proc_port = proc_port;
|
|
obj->control_io = control_io;
|
|
obj->proc_error = false;
|
|
Signal(MainTask, SIGF_SINGLE);
|
|
|
|
// Main loop
|
|
for (;;) {
|
|
|
|
// Wait for I/O and messages (CTRL_C is used for quitting the task)
|
|
ULONG sig = Wait(proc_port_mask | io_mask | SIGBREAKF_CTRL_C);
|
|
|
|
// Main task wants to quit us
|
|
if (sig & SIGBREAKF_CTRL_C)
|
|
break;
|
|
|
|
// Main task sent a command to us
|
|
if (sig & proc_port_mask) {
|
|
struct SerMessage *msg;
|
|
while (msg = (SerMessage *)GetMsg(proc_port)) {
|
|
D(bug("serial_proc received %08lx\n", msg->what));
|
|
switch (msg->what) {
|
|
case MSG_QUERY:
|
|
control_io->IOSer.io_Command = SDCMD_QUERY;
|
|
DoIO((struct IORequest *)control_io);
|
|
D(bug(" query returned %08lx, actual %08lx\n", control_io->IOSer.io_Error, control_io->IOSer.io_Actual));
|
|
break;
|
|
|
|
case MSG_SET_PARAMS:
|
|
// Only send SDCMD_SETPARAMS when configuration has changed
|
|
if (memcmp(orig_params, &(control_io->io_CtlChar), (uint8 *)&(control_io->io_Status) - (uint8 *)&(control_io->io_CtlChar))) {
|
|
memcpy(orig_params, &(control_io->io_CtlChar), (uint8 *)&(control_io->io_Status) - (uint8 *)&(control_io->io_CtlChar));
|
|
memcpy(&(read_io->io_CtlChar), &(control_io->io_CtlChar), (uint8 *)&(control_io->io_Status) - (uint8 *)&(control_io->io_CtlChar));
|
|
memcpy(&(write_io->io_CtlChar), &(control_io->io_CtlChar), (uint8 *)&(control_io->io_Status) - (uint8 *)&(control_io->io_CtlChar));
|
|
control_io->IOSer.io_Command = SDCMD_SETPARAMS;
|
|
D(bug(" params %08lx %08lx %08lx %08lx %08lx %08lx\n", control_io->io_CtlChar, control_io->io_RBufLen, control_io->io_ExtFlags, control_io->io_Baud, control_io->io_BrkTime, *(uint32 *)((uint8 *)control_io + 76)));
|
|
DoIO((struct IORequest *)control_io);
|
|
D(bug(" set_parms returned %08lx\n", control_io->IOSer.io_Error));
|
|
}
|
|
break;
|
|
|
|
case MSG_SET_PAR_PARAMS:
|
|
control_io->IOSer.io_Command = PDCMD_SETPARAMS;
|
|
DoIO((struct IORequest *)control_io);
|
|
D(bug(" set_par_parms returned %08lx\n", control_io->IOSer.io_Error));
|
|
break;
|
|
|
|
case MSG_BREAK:
|
|
control_io->IOSer.io_Command = SDCMD_BREAK;
|
|
DoIO((struct IORequest *)control_io);
|
|
D(bug(" break returned %08lx\n", control_io->IOSer.io_Error));
|
|
break;
|
|
|
|
case MSG_RESET:
|
|
control_io->IOSer.io_Command = CMD_RESET;
|
|
DoIO((struct IORequest *)control_io);
|
|
D(bug(" reset returned %08lx\n", control_io->IOSer.io_Error));
|
|
break;
|
|
|
|
case MSG_KILL_IO:
|
|
AbortIO((struct IORequest *)read_io);
|
|
AbortIO((struct IORequest *)write_io);
|
|
WaitIO((struct IORequest *)read_io);
|
|
WaitIO((struct IORequest *)write_io);
|
|
obj->read_pending = obj->write_pending = false;
|
|
obj->read_done = obj->write_done = false;
|
|
break;
|
|
|
|
case MSG_PRIME_IN:
|
|
read_io->IOSer.io_Message.mn_Node.ln_Name = (char *)msg->pb;
|
|
read_io->IOSer.io_Data = Mac2HostAddr(ReadMacInt32(msg->pb + ioBuffer));
|
|
read_io->IOSer.io_Length = ReadMacInt32(msg->pb + ioReqCount);
|
|
read_io->IOSer.io_Actual = 0;
|
|
read_io->IOSer.io_Command = CMD_READ;
|
|
D(bug("serial_proc receiving %ld bytes from %08lx\n", read_io->IOSer.io_Length, read_io->IOSer.io_Data));
|
|
SendIO((struct IORequest *)read_io);
|
|
break;
|
|
|
|
case MSG_PRIME_OUT: {
|
|
write_io->IOSer.io_Message.mn_Node.ln_Name = (char *)msg->pb;
|
|
write_io->IOSer.io_Data = Mac2HostAddr(ReadMacInt32(msg->pb + ioBuffer));
|
|
write_io->IOSer.io_Length = ReadMacInt32(msg->pb + ioReqCount);
|
|
write_io->IOSer.io_Actual = 0;
|
|
write_io->IOSer.io_Command = CMD_WRITE;
|
|
D(bug("serial_proc transmitting %ld bytes from %08lx\n", write_io->IOSer.io_Length, write_io->IOSer.io_Data));
|
|
#if MONITOR
|
|
bug("Sending serial data:\n");
|
|
uint8 *adr = Mac2HostAddr(ReadMacInt32(msg->pb + ioBuffer));
|
|
for (int i=0; i<len; i++) {
|
|
bug("%02lx ", adr[i]);
|
|
}
|
|
bug("\n");
|
|
#endif
|
|
SendIO((struct IORequest *)write_io);
|
|
break;
|
|
}
|
|
}
|
|
D(bug(" serial_proc replying\n"));
|
|
ReplyMsg(msg);
|
|
}
|
|
}
|
|
|
|
// I/O operation completed
|
|
if (sig & io_mask) {
|
|
struct IOExtSer *io;
|
|
while (io = (struct IOExtSer *)GetMsg(io_port)) {
|
|
if (io == read_io) {
|
|
D(bug("read_io complete, %ld bytes received, error %ld\n", read_io->IOSer.io_Actual, read_io->IOSer.io_Error));
|
|
uint32 pb = (uint32)read_io->IOSer.io_Message.mn_Node.ln_Name;
|
|
#if MONITOR
|
|
bug("Receiving serial data:\n");
|
|
uint8 *adr = Mac2HostAddr(ReadMacInt32(msg->pb + ioBuffer));
|
|
for (int i=0; i<read_io->IOSer.io_Actual; i++) {
|
|
bug("%02lx ", adr[i]);
|
|
}
|
|
bug("\n");
|
|
#endif
|
|
WriteMacInt32(pb + ioActCount, read_io->IOSer.io_Actual);
|
|
obj->conv_error(read_io, obj->input_dt);
|
|
obj->read_done = true;
|
|
SetInterruptFlag(INTFLAG_SERIAL);
|
|
TriggerInterrupt();
|
|
} else if (io == write_io) {
|
|
D(bug("write_io complete, %ld bytes sent, error %ld\n", write_io->IOSer.io_Actual, write_io->IOSer.io_Error));
|
|
uint32 pb = (uint32)write_io->IOSer.io_Message.mn_Node.ln_Name;
|
|
WriteMacInt32(pb + ioActCount, write_io->IOSer.io_Actual);
|
|
obj->conv_error(write_io, obj->output_dt);
|
|
obj->write_done = true;
|
|
SetInterruptFlag(INTFLAG_SERIAL);
|
|
TriggerInterrupt();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
quit:
|
|
|
|
// Close everything
|
|
if (opened) {
|
|
if (CheckIO((struct IORequest *)write_io) == 0) {
|
|
AbortIO((struct IORequest *)write_io);
|
|
WaitIO((struct IORequest *)write_io);
|
|
}
|
|
if (CheckIO((struct IORequest *)read_io) == 0) {
|
|
AbortIO((struct IORequest *)read_io);
|
|
WaitIO((struct IORequest *)read_io);
|
|
}
|
|
CloseDevice((struct IORequest *)read_io);
|
|
}
|
|
if (control_io)
|
|
DeleteIORequest(control_io);
|
|
if (write_io)
|
|
DeleteIORequest(write_io);
|
|
if (read_io)
|
|
DeleteIORequest(read_io);
|
|
if (control_port)
|
|
DeleteMsgPort(control_port);
|
|
if (io_port)
|
|
DeleteMsgPort(io_port);
|
|
|
|
// Send signal to main task to confirm termination
|
|
Forbid();
|
|
Signal(MainTask, SIGF_SINGLE);
|
|
}
|