ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/serial_unix.cpp
Revision: 1.12
Committed: 2002-07-31T16:46:14Z (21 years, 11 months ago) by cebix
Branch: MAIN
CVS Tags: nigel-build-12, nigel-build-13
Changes since 1.11: +113 -34 lines
Log Message:
- it is now possible to make the serial drivers pipe their input/output
  to programs by using a '|' followed by a command line as the modem or
  printer port setting (instead of a device name like '/dev/ttyS0')
  [Brian Johnson]
- the option "--config FILE" tells B2 to use a different config file

File Contents

# Content
1 /*
2 * serial_unix.cpp - Serial device driver, Unix specific stuff
3 *
4 * Basilisk II (C) 1997-2002 Christian Bauer
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "sysdeps.h"
22
23 #include <sys/ioctl.h>
24 #include <sys/stat.h>
25 #include <sys/wait.h>
26 #include <pthread.h>
27 #include <semaphore.h>
28 #include <termios.h>
29 #include <errno.h>
30 #ifdef __linux__
31 #include <linux/lp.h>
32 #include <linux/major.h>
33 #include <linux/kdev_t.h>
34 #endif
35
36 #include "cpu_emulation.h"
37 #include "main.h"
38 #include "macos_util.h"
39 #include "prefs.h"
40 #include "serial.h"
41 #include "serial_defs.h"
42
43 extern "C" {
44 #include "sshpty.h"
45 }
46
47
48 #define DEBUG 0
49 #include "debug.h"
50
51 #define MONITOR 0
52
53
54 // Missing functions
55 #ifndef HAVE_CFMAKERAW
56 static int cfmakeraw(struct termios *termios_p)
57 {
58 termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
59 termios_p->c_oflag &= ~OPOST;
60 termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
61 termios_p->c_cflag &= ~(CSIZE|PARENB);
62 termios_p->c_cflag |= CS8;
63 return 0;
64 }
65 #endif
66
67
68 // Driver private variables
69 class XSERDPort : public SERDPort {
70 public:
71 XSERDPort(const char *dev)
72 {
73 device_name = dev;
74 protocol = serial;
75 fd = -1;
76 pid = 0;
77 input_thread_active = output_thread_active = false;
78
79 Set_pthread_attr(&thread_attr, 2);
80 }
81
82 virtual ~XSERDPort()
83 {
84 if (input_thread_active) {
85 input_thread_cancel = true;
86 #ifdef HAVE_PTHREAD_CANCEL
87 pthread_cancel(input_thread);
88 #endif
89 pthread_join(input_thread, NULL);
90 sem_destroy(&input_signal);
91 input_thread_active = false;
92 }
93 if (output_thread_active) {
94 output_thread_cancel = true;
95 #ifdef HAVE_PTHREAD_CANCEL
96 pthread_cancel(output_thread);
97 #endif
98 pthread_join(output_thread, NULL);
99 sem_destroy(&output_signal);
100 output_thread_active = false;
101 }
102 }
103
104 virtual int16 open(uint16 config);
105 virtual int16 prime_in(uint32 pb, uint32 dce);
106 virtual int16 prime_out(uint32 pb, uint32 dce);
107 virtual int16 control(uint32 pb, uint32 dce, uint16 code);
108 virtual int16 status(uint32 pb, uint32 dce, uint16 code);
109 virtual int16 close(void);
110
111 private:
112 bool open_pty(void);
113 bool configure(uint16 config);
114 void set_handshake(uint32 s, bool with_dtr);
115 static void *input_func(void *arg);
116 static void *output_func(void *arg);
117
118 const char *device_name; // Device name
119 enum {serial, parallel, pty, midi}
120 protocol; // Type of device
121 int fd; // FD of device
122 pid_t pid; // PID of child process
123
124 bool io_killed; // Flag: KillIO called, I/O threads must not call deferred tasks
125 bool quitting; // Flag: Quit threads
126
127 pthread_attr_t thread_attr; // Input/output thread attributes
128
129 bool input_thread_active; // Flag: Input thread installed
130 volatile bool input_thread_cancel; // Flag: Cancel input thread
131 pthread_t input_thread; // Data input thread
132 sem_t input_signal; // Signal for input thread: execute command
133 uint32 input_pb; // Command parameter for input thread
134
135 bool output_thread_active; // Flag: Output thread installed
136 volatile bool output_thread_cancel; // Flag: Cancel output thread
137 pthread_t output_thread; // Data output thread
138 sem_t output_signal; // Signal for output thread: execute command
139 uint32 output_pb; // Command parameter for output thread
140
141 struct termios mode; // Terminal configuration
142 };
143
144
145 /*
146 * Initialization
147 */
148
149 void SerialInit(void)
150 {
151 // Read serial preferences and create structs for both ports
152 the_serd_port[0] = new XSERDPort(PrefsFindString("seriala"));
153 the_serd_port[1] = new XSERDPort(PrefsFindString("serialb"));
154 }
155
156
157 /*
158 * Deinitialization
159 */
160
161 void SerialExit(void)
162 {
163 delete (XSERDPort *)the_serd_port[0];
164 delete (XSERDPort *)the_serd_port[1];
165 }
166
167
168 /*
169 * Open serial port
170 */
171
172 int16 XSERDPort::open(uint16 config)
173 {
174 // Don't open NULL name devices
175 if (device_name == NULL)
176 return openErr;
177
178 // Init variables
179 io_killed = false;
180 quitting = false;
181
182 // Open port, according to the syntax of the path
183 if (device_name[0] == '|') {
184 // Open a process via ptys
185 if (!open_pty())
186 goto open_error;
187 }
188 else if (!strcmp(device_name, "midi")) {
189 // MIDI: not yet implemented
190 return openErr;
191 }
192 else {
193 // Device special file
194 fd = ::open(device_name, O_RDWR);
195 if (fd < 0)
196 goto open_error;
197
198 #if defined(__linux__)
199 // Parallel port?
200 struct stat st;
201 if (fstat(fd, &st) == 0)
202 if (S_ISCHR(st.st_mode))
203 protocol = ((MAJOR(st.st_rdev) == LP_MAJOR) ? parallel : serial);
204 #elif defined(__FreeBSD__) || defined(__NetBSD__)
205 // Parallel port?
206 struct stat st;
207 if (fstat(fd, &st) == 0)
208 if (S_ISCHR(st.st_mode))
209 protocol = (((st.st_rdev >> 16) == 16) ? parallel : serial);
210 #endif
211 }
212
213 // Configure port for raw mode
214 if (protocol == serial) {
215 if (tcgetattr(fd, &mode) < 0)
216 goto open_error;
217 cfmakeraw(&mode);
218 mode.c_cflag |= HUPCL;
219 mode.c_cc[VMIN] = 1;
220 mode.c_cc[VTIME] = 0;
221 tcsetattr(fd, TCSAFLUSH, &mode);
222 }
223 configure(config);
224
225 // Start input/output threads
226 input_thread_cancel = false;
227 output_thread_cancel = false;
228 if (sem_init(&input_signal, 0, 0) < 0)
229 goto open_error;
230 if (sem_init(&output_signal, 0, 0) < 0)
231 goto open_error;
232 input_thread_active = (pthread_create(&input_thread, &thread_attr, input_func, this) == 0);
233 output_thread_active = (pthread_create(&output_thread, &thread_attr, output_func, this) == 0);
234 if (!input_thread_active || !output_thread_active)
235 goto open_error;
236 return noErr;
237
238 open_error:
239 if (input_thread_active) {
240 input_thread_cancel = true;
241 #ifdef HAVE_PTHREAD_CANCEL
242 pthread_cancel(input_thread);
243 #endif
244 pthread_join(input_thread, NULL);
245 sem_destroy(&input_signal);
246 input_thread_active = false;
247 }
248 if (output_thread_active) {
249 output_thread_cancel = true;
250 #ifdef HAVE_PTHREAD_CANCEL
251 pthread_cancel(output_thread);
252 #endif
253 pthread_join(output_thread, NULL);
254 sem_destroy(&output_signal);
255 output_thread_active = false;
256 }
257 if (fd > 0) {
258 ::close(fd);
259 fd = -1;
260 }
261 return openErr;
262 }
263
264
265 /*
266 * Read data from port
267 */
268
269 int16 XSERDPort::prime_in(uint32 pb, uint32 dce)
270 {
271 // Send input command to input_thread
272 read_done = false;
273 read_pending = true;
274 input_pb = pb;
275 WriteMacInt32(input_dt + serdtDCE, dce);
276 sem_post(&input_signal);
277 return 1; // Command in progress
278 }
279
280
281 /*
282 * Write data to port
283 */
284
285 int16 XSERDPort::prime_out(uint32 pb, uint32 dce)
286 {
287 // Send output command to output_thread
288 write_done = false;
289 write_pending = true;
290 output_pb = pb;
291 WriteMacInt32(output_dt + serdtDCE, dce);
292 sem_post(&output_signal);
293 return 1; // Command in progress
294 }
295
296
297 /*
298 * Control calls
299 */
300
301 int16 XSERDPort::control(uint32 pb, uint32 dce, uint16 code)
302 {
303 switch (code) {
304 case 1: // KillIO
305 io_killed = true;
306 if (protocol == serial)
307 tcflush(fd, TCIOFLUSH);
308 while (read_pending || write_pending)
309 usleep(10000);
310 io_killed = false;
311 return noErr;
312
313 case kSERDConfiguration:
314 if (configure(ReadMacInt16(pb + csParam)))
315 return noErr;
316 else
317 return paramErr;
318
319 case kSERDInputBuffer:
320 return noErr; // Not supported under Unix
321
322 case kSERDSerHShake:
323 set_handshake(pb + csParam, false);
324 return noErr;
325
326 case kSERDSetBreak:
327 if (protocol == serial)
328 tcsendbreak(fd, 0);
329 return noErr;
330
331 case kSERDClearBreak:
332 return noErr;
333
334 case kSERDBaudRate: {
335 if (protocol != serial)
336 return noErr;
337 uint16 rate = ReadMacInt16(pb + csParam);
338 speed_t baud_rate;
339 if (rate <= 50) {
340 rate = 50; baud_rate = B50;
341 } else if (rate <= 75) {
342 rate = 75; baud_rate = B75;
343 } else if (rate <= 110) {
344 rate = 110; baud_rate = B110;
345 } else if (rate <= 134) {
346 rate = 134; baud_rate = B134;
347 } else if (rate <= 150) {
348 rate = 150; baud_rate = B150;
349 } else if (rate <= 200) {
350 rate = 200; baud_rate = B200;
351 } else if (rate <= 300) {
352 rate = 300; baud_rate = B300;
353 } else if (rate <= 600) {
354 rate = 600; baud_rate = B600;
355 } else if (rate <= 1200) {
356 rate = 1200; baud_rate = B1200;
357 } else if (rate <= 1800) {
358 rate = 1800; baud_rate = B1800;
359 } else if (rate <= 2400) {
360 rate = 2400; baud_rate = B2400;
361 } else if (rate <= 4800) {
362 rate = 4800; baud_rate = B4800;
363 } else if (rate <= 9600) {
364 rate = 9600; baud_rate = B9600;
365 } else if (rate <= 19200) {
366 rate = 19200; baud_rate = B19200;
367 } else if (rate <= 38400) {
368 rate = 38400; baud_rate = B38400;
369 } else if (rate <= 57600) {
370 rate = 57600; baud_rate = B57600;
371 } else {
372 // Just for safety in case someone wants a rate between 57600 and 65535
373 rate = 57600; baud_rate = B57600;
374 }
375 WriteMacInt16(pb + csParam, rate);
376 cfsetispeed(&mode, baud_rate);
377 cfsetospeed(&mode, baud_rate);
378 tcsetattr(fd, TCSANOW, &mode);
379 return noErr;
380 }
381
382 case kSERDHandshake:
383 case kSERDHandshakeRS232:
384 set_handshake(pb + csParam, true);
385 return noErr;
386
387 case kSERDMiscOptions:
388 if (protocol != serial)
389 return noErr;
390 if (ReadMacInt8(pb + csParam) & kOptionPreserveDTR)
391 mode.c_cflag &= ~HUPCL;
392 else
393 mode.c_cflag |= HUPCL;
394 tcsetattr(fd, TCSANOW, &mode);
395 return noErr;
396
397 case kSERDAssertDTR: {
398 if (protocol != serial)
399 return noErr;
400 unsigned int status = TIOCM_DTR;
401 ioctl(fd, TIOCMBIS, &status);
402 return noErr;
403 }
404
405 case kSERDNegateDTR: {
406 if (protocol != serial)
407 return noErr;
408 unsigned int status = TIOCM_DTR;
409 ioctl(fd, TIOCMBIC, &status);
410 return noErr;
411 }
412
413 case kSERDSetPEChar:
414 case kSERDSetPEAltChar:
415 return noErr; // Not supported under Unix
416
417 case kSERDResetChannel:
418 if (protocol == serial)
419 tcflush(fd, TCIOFLUSH);
420 return noErr;
421
422 case kSERDAssertRTS: {
423 if (protocol != serial)
424 return noErr;
425 unsigned int status = TIOCM_RTS;
426 ioctl(fd, TIOCMBIS, &status);
427 return noErr;
428 }
429
430 case kSERDNegateRTS: {
431 if (protocol != serial)
432 return noErr;
433 unsigned int status = TIOCM_RTS;
434 ioctl(fd, TIOCMBIC, &status);
435 return noErr;
436 }
437
438 case kSERD115KBaud:
439 if (protocol != serial)
440 return noErr;
441 cfsetispeed(&mode, B115200);
442 cfsetospeed(&mode, B115200);
443 tcsetattr(fd, TCSANOW, &mode);
444 return noErr;
445
446 case kSERD230KBaud:
447 case kSERDSetHighSpeed:
448 if (protocol != serial)
449 return noErr;
450 cfsetispeed(&mode, B230400);
451 cfsetospeed(&mode, B230400);
452 tcsetattr(fd, TCSANOW, &mode);
453 return noErr;
454
455 default:
456 printf("WARNING: SerialControl(): unimplemented control code %d\n", code);
457 return controlErr;
458 }
459 }
460
461
462 /*
463 * Status calls
464 */
465
466 int16 XSERDPort::status(uint32 pb, uint32 dce, uint16 code)
467 {
468 switch (code) {
469 case kSERDInputCount: {
470 int num;
471 ioctl(fd, FIONREAD, &num);
472 WriteMacInt32(pb + csParam, num);
473 return noErr;
474 }
475
476 case kSERDStatus: {
477 uint32 p = pb + csParam;
478 WriteMacInt8(p + staCumErrs, cum_errors);
479 cum_errors = 0;
480 WriteMacInt8(p + staXOffSent, 0);
481 WriteMacInt8(p + staXOffHold, 0);
482 WriteMacInt8(p + staRdPend, read_pending);
483 WriteMacInt8(p + staWrPend, write_pending);
484 if (protocol != serial) {
485 WriteMacInt8(p + staCtsHold, 0);
486 WriteMacInt8(p + staDsrHold, 0);
487 WriteMacInt8(p + staModemStatus, dsrEvent | dcdEvent | ctsEvent);
488 } else {
489 unsigned int status;
490 ioctl(fd, TIOCMGET, &status);
491 WriteMacInt8(p + staCtsHold, status & TIOCM_CTS ? 0 : 1);
492 WriteMacInt8(p + staDsrHold, status & TIOCM_DTR ? 0 : 1);
493 WriteMacInt8(p + staModemStatus,
494 (status & TIOCM_DSR ? dsrEvent : 0)
495 | (status & TIOCM_RI ? riEvent : 0)
496 | (status & TIOCM_CD ? dcdEvent : 0)
497 | (status & TIOCM_CTS ? ctsEvent : 0));
498 }
499 return noErr;
500 }
501
502 default:
503 printf("WARNING: SerialStatus(): unimplemented status code %d\n", code);
504 return statusErr;
505 }
506 }
507
508
509 /*
510 * Close serial port
511 */
512
513 int16 XSERDPort::close()
514 {
515 // Kill threads
516 if (input_thread_active) {
517 quitting = true;
518 sem_post(&input_signal);
519 pthread_join(input_thread, NULL);
520 input_thread_active = false;
521 sem_destroy(&input_signal);
522 }
523 if (output_thread_active) {
524 quitting = true;
525 sem_post(&output_signal);
526 pthread_join(output_thread, NULL);
527 output_thread_active = false;
528 sem_destroy(&output_signal);
529 }
530
531 // Close port
532 if (fd > 0)
533 ::close(fd);
534 fd = -1;
535
536 // Wait for the subprocess to exit
537 if (pid)
538 waitpid(pid, NULL, 0);
539 pid = 0;
540
541 return noErr;
542 }
543
544
545 /*
546 * Open a process via ptys
547 */
548
549 bool XSERDPort::open_pty(void)
550 {
551 // Talk to a process via a pty
552 char slave[128];
553 int slavefd;
554
555 protocol = pty;
556 if (!pty_allocate(&fd, &slavefd, slave, sizeof(slave)))
557 return false;
558
559 fflush(stdout);
560 fflush(stderr);
561 switch (pid = fork()) {
562 case -1: // error
563 return false;
564 break;
565 case 0: // child
566 ::close(fd);
567
568 /* Make the pseudo tty our controlling tty. */
569 pty_make_controlling_tty(&slavefd, slave);
570
571 ::close(0); dup(slavefd); // Use the slave fd for stdin,
572 ::close(1); dup(slavefd); // stdout,
573 ::close(2); dup(slavefd); // and stderr.
574
575 // <should we be more paranoid about closing unused fds?>
576 // <should we drop privileges if running setuid?>
577
578 // Let the shell do the dirty work
579 execlp("/bin/sh", "/bin/sh", "-c", ++device_name, 0);
580
581 // exec failed!
582 printf("serial_open: could not exec %s: %s\n",
583 "/bin/sh", strerror(errno));
584 exit(1);
585 break;
586 default: // parent
587 // Pid was stored above
588 break;
589 }
590
591 return true;
592 }
593
594
595 /*
596 * Configure serial port with MacOS config word
597 */
598
599 bool XSERDPort::configure(uint16 config)
600 {
601 D(bug(" configure %04x\n", config));
602 if (protocol != serial)
603 return true;
604
605 // Set number of stop bits
606 switch (config & 0xc000) {
607 case stop10:
608 mode.c_cflag &= ~CSTOPB;
609 break;
610 case stop20:
611 mode.c_cflag |= CSTOPB;
612 break;
613 default:
614 return false;
615 }
616
617 // Set parity mode
618 switch (config & 0x3000) {
619 case noParity:
620 mode.c_iflag &= ~INPCK;
621 mode.c_oflag &= ~PARENB;
622 break;
623 case oddParity:
624 mode.c_iflag |= INPCK;
625 mode.c_oflag |= PARENB;
626 mode.c_oflag |= PARODD;
627 break;
628 case evenParity:
629 mode.c_iflag |= INPCK;
630 mode.c_oflag |= PARENB;
631 mode.c_oflag &= ~PARODD;
632 break;
633 default:
634 return false;
635 }
636
637 // Set number of data bits
638 switch (config & 0x0c00) {
639 case data5:
640 mode.c_cflag = mode.c_cflag & ~CSIZE | CS5;
641 break;
642 case data6:
643 mode.c_cflag = mode.c_cflag & ~CSIZE | CS6;
644 break;
645 case data7:
646 mode.c_cflag = mode.c_cflag & ~CSIZE | CS7;
647 break;
648 case data8:
649 mode.c_cflag = mode.c_cflag & ~CSIZE | CS8;
650 break;
651 }
652
653 // Set baud rate
654 speed_t baud_rate;
655 switch (config & 0x03ff) {
656 case baud150: baud_rate = B150; break;
657 case baud300: baud_rate = B300; break;
658 case baud600: baud_rate = B600; break;
659 case baud1200: baud_rate = B1200; break;
660 case baud1800: baud_rate = B1800; break;
661 case baud2400: baud_rate = B2400; break;
662 case baud4800: baud_rate = B4800; break;
663 case baud9600: baud_rate = B9600; break;
664 case baud19200: baud_rate = B19200; break;
665 case baud38400: baud_rate = B38400; break;
666 case baud57600: baud_rate = B57600; break;
667 default:
668 return false;
669 }
670 cfsetispeed(&mode, baud_rate);
671 cfsetospeed(&mode, baud_rate);
672 tcsetattr(fd, TCSANOW, &mode);
673 return true;
674 }
675
676
677 /*
678 * Set serial handshaking
679 */
680
681 void XSERDPort::set_handshake(uint32 s, bool with_dtr)
682 {
683 D(bug(" set_handshake %02x %02x %02x %02x %02x %02x %02x %02x\n",
684 ReadMacInt8(s + 0), ReadMacInt8(s + 1), ReadMacInt8(s + 2), ReadMacInt8(s + 3),
685 ReadMacInt8(s + 4), ReadMacInt8(s + 5), ReadMacInt8(s + 6), ReadMacInt8(s + 7)));
686 if (protocol != serial)
687 return;
688
689 if (with_dtr) {
690 if (ReadMacInt8(s + shkFCTS) || ReadMacInt8(s + shkFDTR))
691 mode.c_cflag |= CRTSCTS;
692 else
693 mode.c_cflag &= ~CRTSCTS;
694 } else {
695 if (ReadMacInt8(s + shkFCTS))
696 mode.c_cflag |= CRTSCTS;
697 else
698 mode.c_cflag &= ~CRTSCTS;
699 }
700
701 D(bug(" %sware flow control\n", mode.c_cflag & CRTSCTS ? "hard" : "soft"));
702 tcsetattr(fd, TCSANOW, &mode);
703 }
704
705
706 /*
707 * Data input thread
708 */
709
710 void *XSERDPort::input_func(void *arg)
711 {
712 XSERDPort *s = (XSERDPort *)arg;
713 while (!s->input_thread_cancel) {
714
715 // Wait for commands
716 sem_wait(&s->input_signal);
717 if (s->quitting)
718 break;
719
720 // Execute command
721 void *buf = Mac2HostAddr(ReadMacInt32(s->input_pb + ioBuffer));
722 uint32 length = ReadMacInt32(s->input_pb + ioReqCount);
723 D(bug("input_func waiting for %ld bytes of data...\n", length));
724 int32 actual = read(s->fd, buf, length);
725 D(bug(" %ld bytes received\n", actual));
726
727 #if MONITOR
728 bug("Receiving serial data:\n");
729 uint8 *adr = (uint8 *)buf;
730 for (int i=0; i<actual; i++) {
731 bug("%02x ", adr[i]);
732 }
733 bug("\n");
734 #endif
735
736 // KillIO called? Then simply return
737 if (s->io_killed) {
738
739 WriteMacInt16(s->input_pb + ioResult, uint16(abortErr));
740 WriteMacInt32(s->input_pb + ioActCount, 0);
741 s->read_pending = s->read_done = false;
742
743 } else {
744
745 // Set error code
746 if (actual >= 0) {
747 WriteMacInt32(s->input_pb + ioActCount, actual);
748 WriteMacInt32(s->input_dt + serdtResult, noErr);
749 } else {
750 WriteMacInt32(s->input_pb + ioActCount, 0);
751 WriteMacInt32(s->input_dt + serdtResult, uint16(readErr));
752 }
753
754 // Trigger serial interrupt
755 D(bug(" triggering serial interrupt\n"));
756 s->read_done = true;
757 SetInterruptFlag(INTFLAG_SERIAL);
758 TriggerInterrupt();
759 }
760 }
761 return NULL;
762 }
763
764
765 /*
766 * Data output thread
767 */
768
769 void *XSERDPort::output_func(void *arg)
770 {
771 XSERDPort *s = (XSERDPort *)arg;
772 while (!s->output_thread_cancel) {
773
774 // Wait for commands
775 sem_wait(&s->output_signal);
776 if (s->quitting)
777 break;
778
779 // Execute command
780 void *buf = Mac2HostAddr(ReadMacInt32(s->output_pb + ioBuffer));
781 uint32 length = ReadMacInt32(s->output_pb + ioReqCount);
782 D(bug("output_func transmitting %ld bytes of data...\n", length));
783
784 #if MONITOR
785 bug("Sending serial data:\n");
786 uint8 *adr = (uint8 *)buf;
787 for (int i=0; i<length; i++) {
788 bug("%02x ", adr[i]);
789 }
790 bug("\n");
791 #endif
792
793 int32 actual = write(s->fd, buf, length);
794 D(bug(" %ld bytes transmitted\n", actual));
795
796 // KillIO called? Then simply return
797 if (s->io_killed) {
798
799 WriteMacInt16(s->output_pb + ioResult, uint16(abortErr));
800 WriteMacInt32(s->output_pb + ioActCount, 0);
801 s->write_pending = s->write_done = false;
802
803 } else {
804
805 // Set error code
806 if (actual >= 0) {
807 WriteMacInt32(s->output_pb + ioActCount, actual);
808 WriteMacInt32(s->output_dt + serdtResult, noErr);
809 } else {
810 WriteMacInt32(s->output_pb + ioActCount, 0);
811 WriteMacInt32(s->output_dt + serdtResult, uint16(writErr));
812 }
813
814 // Trigger serial interrupt
815 D(bug(" triggering serial interrupt\n"));
816 s->write_done = true;
817 SetInterruptFlag(INTFLAG_SERIAL);
818 TriggerInterrupt();
819 }
820 }
821 return NULL;
822 }