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