ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/serial_unix.cpp
Revision: 1.15
Committed: 2005-12-04T15:03:11Z (18 years, 11 months ago) by gbeauche
Branch: MAIN
CVS Tags: nigel-build-19, nigel-build-17
Changes since 1.14: +11 -0 lines
Log Message:
Fix Serial build on IRIX, remove the STDC_HEADERS manually defined macro
since it now works (egrep was missing previously, IIRC)

File Contents

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