ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/serial_unix.cpp
Revision: 1.7
Committed: 2001-02-02T20:52:58Z (23 years, 9 months ago) by cebix
Branch: MAIN
CVS Tags: snapshot-17022001
Changes since 1.6: +1 -1 lines
Log Message:
- bumped version number to 0.9
- updated copyright dates

File Contents

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