mirror of
https://github.com/kanjitalk755/macemu.git
synced 2024-06-09 00:29:33 +00:00
874 lines
22 KiB
C++
874 lines
22 KiB
C++
/*
|
|
* serial_beos.cpp - Serial device driver, BeOS 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 <string.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <DeviceKit.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
|
|
|
|
|
|
// Buffer size for kernel-space transfers
|
|
const int TMP_BUF_SIZE = 2048;
|
|
|
|
// These packets are sent to the input/output threads
|
|
const uint32 CMD_READ = 'read';
|
|
const uint32 CMD_WRITE = 'writ';
|
|
const uint32 CMD_QUIT = 'quit';
|
|
|
|
struct ThreadPacket {
|
|
uint32 pb;
|
|
};
|
|
|
|
|
|
// Driver private variables
|
|
class BeSERDPort : public SERDPort {
|
|
public:
|
|
BeSERDPort(const char *dev)
|
|
{
|
|
device_name = dev;
|
|
if (strstr(dev, "parallel")) {
|
|
is_parallel = true;
|
|
fd = -1;
|
|
device = NULL;
|
|
} else {
|
|
is_parallel = false;
|
|
device = new BSerialPort;
|
|
}
|
|
device_sem = create_sem(1, "serial port");
|
|
input_thread = output_thread = 0;
|
|
}
|
|
|
|
virtual ~BeSERDPort()
|
|
{
|
|
status_t l;
|
|
if (input_thread > 0) {
|
|
send_data(input_thread, CMD_QUIT, NULL, 0);
|
|
suspend_thread(input_thread); // Unblock thread
|
|
snooze(1000);
|
|
resume_thread(input_thread);
|
|
while (wait_for_thread(input_thread, &l) == B_INTERRUPTED) ;
|
|
}
|
|
if (output_thread > 0) {
|
|
send_data(output_thread, CMD_QUIT, NULL, 0);
|
|
suspend_thread(output_thread); // Unblock thread
|
|
snooze(1000);
|
|
resume_thread(output_thread);
|
|
while (wait_for_thread(output_thread, &l) == B_INTERRUPTED) ;
|
|
}
|
|
acquire_sem(device_sem);
|
|
delete_sem(device_sem);
|
|
delete device;
|
|
}
|
|
|
|
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);
|
|
static status_t input_func(void *arg);
|
|
static status_t output_func(void *arg);
|
|
|
|
const char *device_name; // Name of BeOS port
|
|
BSerialPort *device; // BeOS port object
|
|
bool is_parallel; // Flag: Port is parallel, use fd
|
|
int fd; // FD for parallel ports
|
|
sem_id device_sem; // BSerialPort arbitration
|
|
|
|
thread_id input_thread; // Data input thread
|
|
thread_id output_thread; // Data output thread
|
|
|
|
bool io_killed; // Flag: KillIO called, I/O threads must not call deferred tasks
|
|
bool drop_dtr_on_close; // Flag: Negate DTR when driver is closed
|
|
|
|
uint8 tmp_in_buf[TMP_BUF_SIZE]; // Buffers for copying from/to kernel space
|
|
uint8 tmp_out_buf[TMP_BUF_SIZE];
|
|
};
|
|
|
|
|
|
#if DEBUG
|
|
static const int baud_rates[] = {
|
|
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 31250
|
|
};
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Initialization
|
|
*/
|
|
|
|
void SerialInit(void)
|
|
{
|
|
// Read serial preferences and create structs for both ports
|
|
the_serd_port[0] = new BeSERDPort(PrefsFindString("seriala"));
|
|
the_serd_port[1] = new BeSERDPort(PrefsFindString("serialb"));
|
|
}
|
|
|
|
|
|
/*
|
|
* Deinitialization
|
|
*/
|
|
|
|
void SerialExit(void)
|
|
{
|
|
delete (BeSERDPort *)the_serd_port[0];
|
|
delete (BeSERDPort *)the_serd_port[1];
|
|
}
|
|
|
|
|
|
/*
|
|
* Open serial port
|
|
*/
|
|
|
|
int16 BeSERDPort::open(uint16 config)
|
|
{
|
|
// Don't open NULL name devices
|
|
if (device_name == NULL)
|
|
return openErr;
|
|
|
|
// Init variables
|
|
io_killed = false;
|
|
drop_dtr_on_close = true;
|
|
|
|
// Open port
|
|
while (acquire_sem(device_sem) == B_INTERRUPTED) ;
|
|
if (is_parallel) {
|
|
char name[256];
|
|
sprintf(name, "/dev/parallel/%s", device_name);
|
|
fd = ::open(name, O_WRONLY);
|
|
if (fd < 0) {
|
|
release_sem(device_sem);
|
|
return openErr;
|
|
}
|
|
} else {
|
|
device->SetFlowControl(B_HARDWARE_CONTROL); // Must be set before port is opened
|
|
if (device->Open(device_name) > 0) {
|
|
device->SetBlocking(true);
|
|
device->SetTimeout(10000000);
|
|
device->SetDTR(true);
|
|
device->SetRTS(true);
|
|
} else {
|
|
release_sem(device_sem);
|
|
return openErr;
|
|
}
|
|
}
|
|
|
|
// Start input/output threads
|
|
release_sem(device_sem);
|
|
configure(config);
|
|
while (acquire_sem(device_sem) == B_INTERRUPTED) ;
|
|
while ((input_thread = spawn_thread(input_func, "Serial Input", B_NORMAL_PRIORITY, this)) == B_INTERRUPTED) ;
|
|
resume_thread(input_thread);
|
|
while ((output_thread = spawn_thread(output_func, "Serial Output", B_NORMAL_PRIORITY, this)) == B_INTERRUPTED) ;
|
|
resume_thread(output_thread);
|
|
release_sem(device_sem);
|
|
return noErr;
|
|
}
|
|
|
|
|
|
/*
|
|
* Read data from port
|
|
*/
|
|
|
|
int16 BeSERDPort::prime_in(uint32 pb, uint32 dce)
|
|
{
|
|
// Send input command to input_thread
|
|
read_done = false;
|
|
read_pending = true;
|
|
ThreadPacket p;
|
|
p.pb = pb;
|
|
WriteMacInt32(input_dt + serdtDCE, dce);
|
|
while (send_data(input_thread, CMD_READ, &p, sizeof(ThreadPacket)) == B_INTERRUPTED) ;
|
|
return 1; // Command in progress
|
|
}
|
|
|
|
|
|
/*
|
|
* Write data to port
|
|
*/
|
|
|
|
int16 BeSERDPort::prime_out(uint32 pb, uint32 dce)
|
|
{
|
|
// Send output command to output_thread
|
|
write_done = false;
|
|
write_pending = true;
|
|
ThreadPacket p;
|
|
p.pb = pb;
|
|
WriteMacInt32(output_dt + serdtDCE, dce);
|
|
while (send_data(output_thread, CMD_WRITE, &p, sizeof(ThreadPacket)) == B_INTERRUPTED) ;
|
|
return 1; // Command in progress
|
|
}
|
|
|
|
|
|
/*
|
|
* Control calls
|
|
*/
|
|
|
|
int16 BeSERDPort::control(uint32 pb, uint32 dce, uint16 code)
|
|
{
|
|
switch (code) {
|
|
case 1: // KillIO
|
|
io_killed = true;
|
|
suspend_thread(input_thread); // Unblock threads
|
|
suspend_thread(output_thread);
|
|
snooze(1000);
|
|
resume_thread(input_thread);
|
|
resume_thread(output_thread);
|
|
while (read_pending || write_pending)
|
|
snooze(10000);
|
|
if (!is_parallel) {
|
|
while (acquire_sem(device_sem) == B_INTERRUPTED) ;
|
|
device->ClearInput();
|
|
device->ClearOutput();
|
|
release_sem(device_sem);
|
|
}
|
|
io_killed = false;
|
|
return noErr;
|
|
|
|
case kSERDConfiguration:
|
|
if (configure(ReadMacInt16(pb + csParam)))
|
|
return noErr;
|
|
else
|
|
return paramErr;
|
|
|
|
case kSERDInputBuffer:
|
|
return noErr; // Not supported under BeOS
|
|
|
|
case kSERDSerHShake:
|
|
set_handshake(pb + csParam, false);
|
|
return noErr;
|
|
|
|
case kSERDClearBreak:
|
|
case kSERDSetBreak:
|
|
return noErr; // Not supported under BeOS
|
|
|
|
case kSERDBaudRate:
|
|
if (!is_parallel) {
|
|
uint16 rate = ReadMacInt16(pb + csParam);
|
|
data_rate baud_rate;
|
|
if (rate <= 50) {
|
|
rate = 50; baud_rate = B_50_BPS;
|
|
} else if (rate <= 75) {
|
|
rate = 75; baud_rate = B_75_BPS;
|
|
} else if (rate <= 110) {
|
|
rate = 110; baud_rate = B_110_BPS;
|
|
} else if (rate <= 134) {
|
|
rate = 134; baud_rate = B_134_BPS;
|
|
} else if (rate <= 150) {
|
|
rate = 150; baud_rate = B_150_BPS;
|
|
} else if (rate <= 200) {
|
|
rate = 200; baud_rate = B_200_BPS;
|
|
} else if (rate <= 300) {
|
|
rate = 300; baud_rate = B_300_BPS;
|
|
} else if (rate <= 600) {
|
|
rate = 600; baud_rate = B_600_BPS;
|
|
} else if (rate <= 1200) {
|
|
rate = 1200; baud_rate = B_1200_BPS;
|
|
} else if (rate <= 1800) {
|
|
rate = 1800; baud_rate = B_1800_BPS;
|
|
} else if (rate <= 2400) {
|
|
rate = 2400; baud_rate = B_2400_BPS;
|
|
} else if (rate <= 4800) {
|
|
rate = 4800; baud_rate = B_4800_BPS;
|
|
} else if (rate <= 9600) {
|
|
rate = 9600; baud_rate = B_9600_BPS;
|
|
} else if (rate <= 19200) {
|
|
rate = 19200; baud_rate = B_19200_BPS;
|
|
} else if (rate <= 31250) {
|
|
rate = 31250; baud_rate = B_31250_BPS;
|
|
} else if (rate <= 38400) {
|
|
rate = 38400; baud_rate = B_38400_BPS;
|
|
} else if (rate <= 57600) {
|
|
rate = 57600; baud_rate = B_57600_BPS;
|
|
}
|
|
WriteMacInt16(pb + csParam, rate);
|
|
acquire_sem(device_sem);
|
|
if (device->SetDataRate(baud_rate) == B_OK) {
|
|
release_sem(device_sem);
|
|
return noErr;
|
|
} else {
|
|
release_sem(device_sem);
|
|
return paramErr;
|
|
}
|
|
} else
|
|
return noErr;
|
|
|
|
case kSERDHandshake:
|
|
case kSERDHandshakeRS232:
|
|
set_handshake(pb + csParam, true);
|
|
return noErr;
|
|
|
|
case kSERDClockMIDI:
|
|
if (!is_parallel) {
|
|
while (acquire_sem(device_sem) == B_INTERRUPTED) ;
|
|
device->SetParityMode(B_NO_PARITY);
|
|
device->SetDataBits(B_DATA_BITS_8);
|
|
device->SetStopBits(B_STOP_BITS_1);
|
|
if (device->SetDataRate(B_31250_BPS) == B_OK) {
|
|
release_sem(device_sem);
|
|
return noErr;
|
|
} else {
|
|
release_sem(device_sem);
|
|
return paramErr;
|
|
}
|
|
} else
|
|
return noErr;
|
|
|
|
case kSERDMiscOptions:
|
|
drop_dtr_on_close = !(ReadMacInt8(pb + csParam) & kOptionPreserveDTR);
|
|
return noErr;
|
|
|
|
case kSERDAssertDTR:
|
|
if (!is_parallel) {
|
|
while (acquire_sem(device_sem) == B_INTERRUPTED) ;
|
|
device->SetDTR(true);
|
|
release_sem(device_sem);
|
|
}
|
|
return noErr;
|
|
|
|
case kSERDNegateDTR:
|
|
if (!is_parallel) {
|
|
while (acquire_sem(device_sem) == B_INTERRUPTED) ;
|
|
device->SetDTR(false);
|
|
release_sem(device_sem);
|
|
}
|
|
return noErr;
|
|
|
|
case kSERDSetPEChar:
|
|
case kSERDSetPEAltChar:
|
|
return noErr; // Not supported under BeOS
|
|
|
|
case kSERDResetChannel:
|
|
if (!is_parallel) {
|
|
while (acquire_sem(device_sem) == B_INTERRUPTED) ;
|
|
device->ClearInput();
|
|
device->ClearOutput();
|
|
release_sem(device_sem);
|
|
}
|
|
return noErr;
|
|
|
|
case kSERDAssertRTS:
|
|
if (!is_parallel) {
|
|
while (acquire_sem(device_sem) == B_INTERRUPTED) ;
|
|
device->SetRTS(true);
|
|
release_sem(device_sem);
|
|
}
|
|
return noErr;
|
|
|
|
case kSERDNegateRTS:
|
|
if (!is_parallel) {
|
|
while (acquire_sem(device_sem) == B_INTERRUPTED) ;
|
|
device->SetRTS(false);
|
|
release_sem(device_sem);
|
|
}
|
|
return noErr;
|
|
|
|
case kSERD115KBaud:
|
|
if (!is_parallel) {
|
|
while (acquire_sem(device_sem) == B_INTERRUPTED) ;
|
|
if (device->DataRate() != B_115200_BPS)
|
|
if (device->SetDataRate(B_115200_BPS) != B_OK) {
|
|
release_sem(device_sem);
|
|
return paramErr;
|
|
}
|
|
release_sem(device_sem);
|
|
}
|
|
return noErr;
|
|
|
|
case kSERD230KBaud:
|
|
case kSERDSetHighSpeed:
|
|
if (!is_parallel) {
|
|
while (acquire_sem(device_sem) == B_INTERRUPTED) ;
|
|
if (device->DataRate() != B_230400_BPS)
|
|
if (device->SetDataRate(B_230400_BPS) != B_OK) {
|
|
release_sem(device_sem);
|
|
return paramErr;
|
|
}
|
|
release_sem(device_sem);
|
|
}
|
|
return noErr;
|
|
|
|
default:
|
|
printf("WARNING: SerialControl(): unimplemented control code %d\n", code);
|
|
return controlErr;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Status calls
|
|
*/
|
|
|
|
int16 BeSERDPort::status(uint32 pb, uint32 dce, uint16 code)
|
|
{
|
|
switch (code) {
|
|
case kSERDInputCount:
|
|
WriteMacInt32(pb + csParam, 0);
|
|
if (!is_parallel) {
|
|
int32 num = 0;
|
|
while (acquire_sem(device_sem) == B_INTERRUPTED) ;
|
|
device->NumCharsAvailable(&num);
|
|
release_sem(device_sem);
|
|
D(bug(" %d bytes in buffer\n", 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 (is_parallel) {
|
|
WriteMacInt8(p + staCtsHold, 0);
|
|
WriteMacInt8(p + staDsrHold, 0);
|
|
WriteMacInt8(p + staModemStatus, dsrEvent | dcdEvent | ctsEvent);
|
|
} else {
|
|
while (acquire_sem(device_sem) == B_INTERRUPTED) ;
|
|
WriteMacInt8(p + staCtsHold, !device->IsCTS());
|
|
WriteMacInt8(p + staDsrHold, !device->IsDSR());
|
|
WriteMacInt8(p + staModemStatus,
|
|
(device->IsDSR() ? dsrEvent : 0)
|
|
| (device->IsRI() ? riEvent : 0)
|
|
| (device->IsDCD() ? dcdEvent : 0)
|
|
| (device->IsCTS() ? ctsEvent : 0));
|
|
release_sem(device_sem);
|
|
}
|
|
return noErr;
|
|
}
|
|
|
|
default:
|
|
printf("WARNING: SerialStatus(): unimplemented status code %d\n", code);
|
|
return statusErr;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Close serial port
|
|
*/
|
|
|
|
int16 BeSERDPort::close()
|
|
{
|
|
// Kill threads
|
|
status_t l;
|
|
io_killed = true;
|
|
if (input_thread > 0) {
|
|
while (send_data(input_thread, CMD_QUIT, NULL, 0) == B_INTERRUPTED) ;
|
|
if (read_pending) {
|
|
suspend_thread(input_thread); // Unblock thread
|
|
snooze(1000);
|
|
resume_thread(input_thread);
|
|
}
|
|
while (wait_for_thread(input_thread, &l) == B_INTERRUPTED) ;
|
|
}
|
|
if (output_thread > 0) {
|
|
while (send_data(output_thread, CMD_QUIT, NULL, 0) == B_INTERRUPTED) ;
|
|
if (write_pending) {
|
|
suspend_thread(output_thread); // Unblock thread
|
|
snooze(1000);
|
|
resume_thread(output_thread);
|
|
}
|
|
while (wait_for_thread(output_thread, &l) == B_INTERRUPTED) ;
|
|
}
|
|
input_thread = output_thread = 0;
|
|
|
|
// Close port
|
|
while (acquire_sem(device_sem) == B_INTERRUPTED) ;
|
|
if (is_parallel) {
|
|
::close(fd);
|
|
fd = -1;
|
|
} else {
|
|
if (drop_dtr_on_close)
|
|
device->SetDTR(false);
|
|
device->Close();
|
|
}
|
|
release_sem(device_sem);
|
|
return noErr;
|
|
}
|
|
|
|
|
|
/*
|
|
* Configure serial port with MacOS config word
|
|
*/
|
|
|
|
bool BeSERDPort::configure(uint16 config)
|
|
{
|
|
D(bug(" configure %04x\n", config));
|
|
if (is_parallel)
|
|
return true;
|
|
|
|
while (acquire_sem(device_sem) == B_INTERRUPTED) ;
|
|
|
|
// Set number of stop bits
|
|
switch (config & 0xc000) {
|
|
case stop10:
|
|
if (device->StopBits() != B_STOP_BITS_1)
|
|
device->SetStopBits(B_STOP_BITS_1);
|
|
break;
|
|
case stop20:
|
|
if (device->StopBits() != B_STOP_BITS_2)
|
|
device->SetStopBits(B_STOP_BITS_2);
|
|
break;
|
|
default:
|
|
release_sem(device_sem);
|
|
return false;
|
|
}
|
|
|
|
// Set parity mode
|
|
switch (config & 0x3000) {
|
|
case noParity:
|
|
if (device->ParityMode() != B_NO_PARITY)
|
|
device->SetParityMode(B_NO_PARITY);
|
|
break;
|
|
case oddParity:
|
|
if (device->ParityMode() != B_ODD_PARITY)
|
|
device->SetParityMode(B_ODD_PARITY);
|
|
break;
|
|
case evenParity:
|
|
if (device->ParityMode() != B_EVEN_PARITY)
|
|
device->SetParityMode(B_EVEN_PARITY);
|
|
break;
|
|
default:
|
|
release_sem(device_sem);
|
|
return false;
|
|
}
|
|
|
|
// Set number of data bits
|
|
switch (config & 0x0c00) {
|
|
case data7:
|
|
if (device->DataBits() != B_DATA_BITS_7)
|
|
device->SetDataBits(B_DATA_BITS_7);
|
|
break;
|
|
case data8:
|
|
if (device->DataBits() != B_DATA_BITS_8)
|
|
device->SetDataBits(B_DATA_BITS_8);
|
|
break;
|
|
default:
|
|
release_sem(device_sem);
|
|
return false;
|
|
}
|
|
|
|
// Set baud rate
|
|
data_rate baud_rate;
|
|
switch (config & 0x03ff) {
|
|
case baud150: baud_rate = B_150_BPS; break;
|
|
case baud300: baud_rate = B_300_BPS; break;
|
|
case baud600: baud_rate = B_600_BPS; break;
|
|
case baud1200: baud_rate = B_1200_BPS; break;
|
|
case baud1800: baud_rate = B_1800_BPS; break;
|
|
case baud2400: baud_rate = B_2400_BPS; break;
|
|
case baud4800: baud_rate = B_4800_BPS; break;
|
|
case baud9600: baud_rate = B_9600_BPS; break;
|
|
case baud19200: baud_rate = B_19200_BPS; break;
|
|
case baud38400: baud_rate = B_38400_BPS; break;
|
|
case baud57600: baud_rate = B_57600_BPS; break;
|
|
default:
|
|
release_sem(device_sem);
|
|
return false;
|
|
}
|
|
|
|
D(bug(" baud rate %d, %d stop bits, %s parity, %d data bits\n", baud_rates[baud_rate], device->StopBits() == B_STOP_BITS_1 ? 1 : 2, device->ParityMode() == B_NO_PARITY ? "no" : device->ParityMode() == B_ODD_PARITY ? "odd" : "even", device->DataBits() == B_DATA_BITS_7 ? 7 : 8));
|
|
if (device->DataRate() != baud_rate) {
|
|
bool res = device->SetDataRate(baud_rate) == B_OK;
|
|
release_sem(device_sem);
|
|
return res;
|
|
} else {
|
|
release_sem(device_sem);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Set serial handshaking
|
|
*/
|
|
|
|
void BeSERDPort::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 (is_parallel)
|
|
return;
|
|
|
|
uint32 flow;
|
|
if (with_dtr) {
|
|
if (ReadMacInt8(s + shkFCTS) || ReadMacInt8(s + shkFDTR))
|
|
flow = B_HARDWARE_CONTROL;
|
|
else
|
|
flow = B_SOFTWARE_CONTROL;
|
|
} else {
|
|
if (ReadMacInt8(s + shkFCTS))
|
|
flow = B_HARDWARE_CONTROL;
|
|
else
|
|
flow = B_SOFTWARE_CONTROL;
|
|
}
|
|
|
|
D(bug(" %sware flow control\n", flow == B_HARDWARE_CONTROL ? "hard" : "soft"));
|
|
while (acquire_sem(device_sem) == B_INTERRUPTED) ;
|
|
if (device->FlowControl() != flow) {
|
|
device->Close();
|
|
device->SetFlowControl(flow);
|
|
device->Open(device_name);
|
|
}
|
|
release_sem(device_sem);
|
|
}
|
|
|
|
|
|
/*
|
|
* Data input thread
|
|
*/
|
|
|
|
status_t BeSERDPort::input_func(void *arg)
|
|
{
|
|
BeSERDPort *s = (BeSERDPort *)arg;
|
|
for (;;) {
|
|
|
|
// Wait for commands
|
|
thread_id sender;
|
|
ThreadPacket p;
|
|
uint32 code = receive_data(&sender, &p, sizeof(ThreadPacket));
|
|
if (code == CMD_QUIT)
|
|
break;
|
|
if (code != CMD_READ)
|
|
continue;
|
|
|
|
// Execute command
|
|
void *buf = Mac2HostAddr(ReadMacInt32(p.pb + ioBuffer));
|
|
uint32 length = ReadMacInt32(p.pb + ioReqCount);
|
|
D(bug("input_func waiting for %ld bytes of data...\n", length));
|
|
int32 actual;
|
|
|
|
// Buffer in kernel space?
|
|
if ((uint32)buf < 0x80000000) {
|
|
|
|
// Yes, transfer via buffer
|
|
actual = 0;
|
|
while (length) {
|
|
uint32 transfer_size = (length > TMP_BUF_SIZE) ? TMP_BUF_SIZE : length;
|
|
int32 transferred;
|
|
acquire_sem(s->device_sem);
|
|
if (s->is_parallel) {
|
|
if ((transferred = read(s->fd, s->tmp_in_buf, transfer_size)) < 0 || s->io_killed) {
|
|
// Error
|
|
actual = transferred;
|
|
release_sem(s->device_sem);
|
|
break;
|
|
}
|
|
} else {
|
|
if ((transferred = s->device->Read(s->tmp_in_buf, transfer_size)) < 0 || s->io_killed) {
|
|
// Error
|
|
actual = transferred;
|
|
release_sem(s->device_sem);
|
|
break;
|
|
}
|
|
}
|
|
release_sem(s->device_sem);
|
|
memcpy(buf, s->tmp_in_buf, transferred);
|
|
buf = (void *)((uint8 *)buf + transferred);
|
|
length -= transferred;
|
|
actual += transferred;
|
|
}
|
|
|
|
} else {
|
|
|
|
// No, transfer directly
|
|
acquire_sem(s->device_sem);
|
|
if (s->is_parallel)
|
|
actual = read(s->fd, buf, length);
|
|
else
|
|
actual = s->device->Read(buf, length);
|
|
release_sem(s->device_sem);
|
|
}
|
|
|
|
D(bug(" %ld bytes received\n", actual));
|
|
|
|
#if MONITOR
|
|
bug("Receiving serial data:\n");
|
|
uint8 *adr = Mac2HostAddr(ReadMacInt32(p.pb + ioBuffer));
|
|
for (int i=0; i<actual; i++) {
|
|
bug("%02x ", adr[i]);
|
|
}
|
|
bug("\n");
|
|
#endif
|
|
|
|
// KillIO called? Then simply return
|
|
if (s->io_killed) {
|
|
|
|
WriteMacInt16(p.pb + ioResult, abortErr);
|
|
WriteMacInt32(p.pb + ioActCount, 0);
|
|
s->read_pending = s->read_done = false;
|
|
|
|
} else {
|
|
|
|
// Set error code
|
|
if (actual >= 0) {
|
|
WriteMacInt32(p.pb + ioActCount, actual);
|
|
WriteMacInt32(s->input_dt + serdtResult, noErr);
|
|
} else {
|
|
WriteMacInt32(p.pb + ioActCount, 0);
|
|
WriteMacInt32(s->input_dt + serdtResult, readErr);
|
|
}
|
|
|
|
// Trigger serial interrupt
|
|
D(bug(" triggering serial interrupt\n"));
|
|
s->read_done = true;
|
|
SetInterruptFlag(INTFLAG_SERIAL);
|
|
TriggerInterrupt();
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Data output thread
|
|
*/
|
|
|
|
status_t BeSERDPort::output_func(void *arg)
|
|
{
|
|
BeSERDPort *s = (BeSERDPort *)arg;
|
|
for (;;) {
|
|
|
|
// Wait for commands
|
|
thread_id sender;
|
|
ThreadPacket p;
|
|
uint32 code = receive_data(&sender, &p, sizeof(ThreadPacket));
|
|
if (code == CMD_QUIT)
|
|
break;
|
|
if (code != CMD_WRITE)
|
|
continue;
|
|
|
|
// Execute command
|
|
void *buf = Mac2HostAddr(ReadMacInt32(p.pb + ioBuffer));
|
|
uint32 length = ReadMacInt32(p.pb + ioReqCount);
|
|
D(bug("output_func transmitting %ld bytes of data...\n", length));
|
|
int32 actual;
|
|
|
|
#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
|
|
|
|
// Buffer in kernel space?
|
|
if ((uint32)buf < 0x80000000) {
|
|
|
|
// Yes, transfer via buffer
|
|
actual = 0;
|
|
while (length) {
|
|
uint32 transfer_size = (length > TMP_BUF_SIZE) ? TMP_BUF_SIZE : length;
|
|
memcpy(s->tmp_out_buf, buf, transfer_size);
|
|
int32 transferred;
|
|
acquire_sem(s->device_sem);
|
|
if (s->is_parallel) {
|
|
if ((transferred = write(s->fd, s->tmp_out_buf, transfer_size)) < transfer_size || s->io_killed) {
|
|
if (transferred < 0) // Error
|
|
actual = transferred;
|
|
else
|
|
actual += transferred;
|
|
release_sem(s->device_sem);
|
|
break;
|
|
}
|
|
} else {
|
|
if ((transferred = s->device->Write(s->tmp_out_buf, transfer_size)) < transfer_size || s->io_killed) {
|
|
if (transferred < 0) // Error
|
|
actual = transferred;
|
|
else
|
|
actual += transferred;
|
|
release_sem(s->device_sem);
|
|
break;
|
|
}
|
|
}
|
|
release_sem(s->device_sem);
|
|
if (transferred > transfer_size) // R3 parallel port driver bug
|
|
transferred = transfer_size;
|
|
buf = (void *)((uint8 *)buf + transferred);
|
|
length -= transferred;
|
|
actual += transferred;
|
|
}
|
|
|
|
} else {
|
|
|
|
// No, transfer directly
|
|
acquire_sem(s->device_sem);
|
|
if (s->is_parallel)
|
|
actual = write(s->fd, buf, length);
|
|
else
|
|
actual = s->device->Write(buf, length);
|
|
release_sem(s->device_sem);
|
|
if (actual > length) // R3 parallel port driver bug
|
|
actual = length;
|
|
}
|
|
|
|
D(bug(" %ld bytes transmitted\n", actual));
|
|
|
|
// KillIO called? Then simply return
|
|
if (s->io_killed) {
|
|
|
|
WriteMacInt16(p.pb + ioResult, abortErr);
|
|
WriteMacInt32(p.pb + ioActCount, 0);
|
|
s->write_pending = s->write_done = false;
|
|
|
|
} else {
|
|
|
|
// Set error code
|
|
if (actual >= 0) {
|
|
WriteMacInt32(p.pb + ioActCount, actual);
|
|
WriteMacInt32(s->output_dt + serdtResult, noErr);
|
|
} else {
|
|
WriteMacInt32(p.pb + ioActCount, 0);
|
|
WriteMacInt32(s->output_dt + serdtResult, writErr);
|
|
}
|
|
|
|
// Trigger serial interrupt
|
|
D(bug(" triggering serial interrupt\n"));
|
|
s->write_done = true;
|
|
SetInterruptFlag(INTFLAG_SERIAL);
|
|
TriggerInterrupt();
|
|
}
|
|
}
|
|
return 0;
|
|
}
|