macemu/BasiliskII/src/BeOS/serial_beos.cpp
2008-01-01 09:40:36 +00:00

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;
}