ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/serial_unix.cpp
Revision: 1.9
Committed: 2002-01-15T14:58:37Z (22 years, 7 months ago) by cebix
Branch: MAIN
CVS Tags: snapshot-15012002
Changes since 1.8: +1 -1 lines
Log Message:
- documentation updates
- 2001 -> 2002
- version 0.9 -> 1.0

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * serial_unix.cpp - Serial device driver, Unix specific stuff
3     *
4 cebix 1.9 * Basilisk II (C) 1997-2002 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 cebix 1.8 input_thread_cancel = false;
212     output_thread_cancel = false;
213 cebix 1.1 if (sem_init(&input_signal, 0, 0) < 0)
214     goto open_error;
215     if (sem_init(&output_signal, 0, 0) < 0)
216     goto open_error;
217     input_thread_active = (pthread_create(&input_thread, &thread_attr, input_func, this) == 0);
218     output_thread_active = (pthread_create(&output_thread, &thread_attr, output_func, this) == 0);
219     if (!input_thread_active || !output_thread_active)
220     goto open_error;
221     return noErr;
222    
223     open_error:
224     if (input_thread_active) {
225     input_thread_cancel = true;
226     #ifdef HAVE_PTHREAD_CANCEL
227     pthread_cancel(input_thread);
228     #endif
229     pthread_join(input_thread, NULL);
230     sem_destroy(&input_signal);
231     input_thread_active = false;
232     }
233     if (output_thread_active) {
234     output_thread_cancel = true;
235     #ifdef HAVE_PTHREAD_CANCEL
236     pthread_cancel(output_thread);
237     #endif
238     pthread_join(output_thread, NULL);
239     sem_destroy(&output_signal);
240     output_thread_active = false;
241     }
242     if (fd > 0) {
243 cebix 1.4 ::close(fd);
244 cebix 1.1 fd = -1;
245     }
246     return openErr;
247     }
248    
249    
250     /*
251     * Read data from port
252     */
253    
254 cebix 1.3 int16 XSERDPort::prime_in(uint32 pb, uint32 dce)
255 cebix 1.1 {
256     // Send input command to input_thread
257     read_done = false;
258     read_pending = true;
259     input_pb = pb;
260     WriteMacInt32(input_dt + serdtDCE, dce);
261     sem_post(&input_signal);
262     return 1; // Command in progress
263     }
264    
265    
266     /*
267     * Write data to port
268     */
269    
270 cebix 1.3 int16 XSERDPort::prime_out(uint32 pb, uint32 dce)
271 cebix 1.1 {
272     // Send output command to output_thread
273     write_done = false;
274     write_pending = true;
275     output_pb = pb;
276     WriteMacInt32(output_dt + serdtDCE, dce);
277     sem_post(&output_signal);
278     return 1; // Command in progress
279     }
280    
281    
282     /*
283     * Control calls
284     */
285    
286 cebix 1.3 int16 XSERDPort::control(uint32 pb, uint32 dce, uint16 code)
287 cebix 1.1 {
288     switch (code) {
289     case 1: // KillIO
290     io_killed = true;
291     if (!is_parallel)
292     tcflush(fd, TCIOFLUSH);
293     while (read_pending || write_pending)
294     usleep(10000);
295     io_killed = false;
296     return noErr;
297    
298     case kSERDConfiguration:
299     if (configure(ReadMacInt16(pb + csParam)))
300     return noErr;
301     else
302     return paramErr;
303    
304     case kSERDInputBuffer:
305     return noErr; // Not supported under Unix
306    
307     case kSERDSerHShake:
308     set_handshake(pb + csParam, false);
309     return noErr;
310    
311     case kSERDSetBreak:
312     if (!is_parallel)
313     tcsendbreak(fd, 0);
314     return noErr;
315    
316     case kSERDClearBreak:
317     return noErr;
318    
319     case kSERDBaudRate: {
320     if (is_parallel)
321     return noErr;
322     uint16 rate = ReadMacInt16(pb + csParam);
323     speed_t baud_rate;
324     if (rate <= 50) {
325     rate = 50; baud_rate = B50;
326     } else if (rate <= 75) {
327     rate = 75; baud_rate = B75;
328     } else if (rate <= 110) {
329     rate = 110; baud_rate = B110;
330     } else if (rate <= 134) {
331     rate = 134; baud_rate = B134;
332     } else if (rate <= 150) {
333     rate = 150; baud_rate = B150;
334     } else if (rate <= 200) {
335     rate = 200; baud_rate = B200;
336     } else if (rate <= 300) {
337     rate = 300; baud_rate = B300;
338     } else if (rate <= 600) {
339     rate = 600; baud_rate = B600;
340     } else if (rate <= 1200) {
341     rate = 1200; baud_rate = B1200;
342     } else if (rate <= 1800) {
343     rate = 1800; baud_rate = B1800;
344     } else if (rate <= 2400) {
345     rate = 2400; baud_rate = B2400;
346     } else if (rate <= 4800) {
347     rate = 4800; baud_rate = B4800;
348     } else if (rate <= 9600) {
349     rate = 9600; baud_rate = B9600;
350     } else if (rate <= 19200) {
351     rate = 19200; baud_rate = B19200;
352     } else if (rate <= 38400) {
353     rate = 38400; baud_rate = B38400;
354     } else if (rate <= 57600) {
355     rate = 57600; baud_rate = B57600;
356     } else {
357     // Just for safety in case someone wants a rate between 57600 and 65535
358     rate = 57600; baud_rate = B57600;
359     }
360     WriteMacInt16(pb + csParam, rate);
361     cfsetispeed(&mode, B115200);
362     cfsetospeed(&mode, B115200);
363     tcsetattr(fd, TCSANOW, &mode);
364     return noErr;
365     }
366    
367     case kSERDHandshake:
368     case kSERDHandshakeRS232:
369     set_handshake(pb + csParam, true);
370     return noErr;
371    
372     case kSERDMiscOptions:
373     if (is_parallel)
374     return noErr;
375     if (ReadMacInt8(pb + csParam) & kOptionPreserveDTR)
376     mode.c_cflag &= ~HUPCL;
377     else
378     mode.c_cflag |= HUPCL;
379     tcsetattr(fd, TCSANOW, &mode);
380     return noErr;
381    
382     case kSERDAssertDTR: {
383     if (is_parallel)
384     return noErr;
385     unsigned int status = TIOCM_DTR;
386     ioctl(fd, TIOCMBIS, &status);
387     return noErr;
388     }
389    
390     case kSERDNegateDTR: {
391     if (is_parallel)
392     return noErr;
393     unsigned int status = TIOCM_DTR;
394     ioctl(fd, TIOCMBIC, &status);
395     return noErr;
396     }
397    
398     case kSERDSetPEChar:
399     case kSERDSetPEAltChar:
400     return noErr; // Not supported under Unix
401    
402     case kSERDResetChannel:
403     if (!is_parallel)
404     tcflush(fd, TCIOFLUSH);
405     return noErr;
406    
407     case kSERDAssertRTS: {
408     if (is_parallel)
409     return noErr;
410     unsigned int status = TIOCM_RTS;
411     ioctl(fd, TIOCMBIS, &status);
412     return noErr;
413     }
414    
415     case kSERDNegateRTS: {
416     if (is_parallel)
417     return noErr;
418     unsigned int status = TIOCM_RTS;
419     ioctl(fd, TIOCMBIC, &status);
420     return noErr;
421     }
422    
423     case kSERD115KBaud:
424     if (is_parallel)
425     return noErr;
426     cfsetispeed(&mode, B115200);
427     cfsetospeed(&mode, B115200);
428     tcsetattr(fd, TCSANOW, &mode);
429     return noErr;
430    
431     case kSERD230KBaud:
432     case kSERDSetHighSpeed:
433     if (is_parallel)
434     return noErr;
435     cfsetispeed(&mode, B230400);
436     cfsetospeed(&mode, B230400);
437     tcsetattr(fd, TCSANOW, &mode);
438     return noErr;
439    
440     default:
441     printf("WARNING: SerialControl(): unimplemented control code %d\n", code);
442     return controlErr;
443     }
444     }
445    
446    
447     /*
448     * Status calls
449     */
450    
451 cebix 1.3 int16 XSERDPort::status(uint32 pb, uint32 dce, uint16 code)
452 cebix 1.1 {
453     switch (code) {
454     case kSERDInputCount: {
455     int num;
456     ioctl(fd, FIONREAD, &num);
457     WriteMacInt32(pb + csParam, num);
458     return noErr;
459     }
460    
461     case kSERDStatus: {
462     uint32 p = pb + csParam;
463     WriteMacInt8(p + staCumErrs, cum_errors);
464     cum_errors = 0;
465     WriteMacInt8(p + staXOffSent, 0);
466     WriteMacInt8(p + staXOffHold, 0);
467     WriteMacInt8(p + staRdPend, read_pending);
468     WriteMacInt8(p + staWrPend, write_pending);
469     if (is_parallel) {
470     WriteMacInt8(p + staCtsHold, 0);
471     WriteMacInt8(p + staDsrHold, 0);
472     WriteMacInt8(p + staModemStatus, dsrEvent | dcdEvent | ctsEvent);
473     } else {
474     unsigned int status;
475     ioctl(fd, TIOCMGET, &status);
476     WriteMacInt8(p + staCtsHold, status & TIOCM_CTS ? 0 : 1);
477     WriteMacInt8(p + staDsrHold, status & TIOCM_DTR ? 0 : 1);
478     WriteMacInt8(p + staModemStatus,
479     (status & TIOCM_DSR ? dsrEvent : 0)
480     | (status & TIOCM_RI ? riEvent : 0)
481     | (status & TIOCM_CD ? dcdEvent : 0)
482     | (status & TIOCM_CTS ? ctsEvent : 0));
483     }
484     return noErr;
485     }
486    
487     default:
488     printf("WARNING: SerialStatus(): unimplemented status code %d\n", code);
489     return statusErr;
490     }
491     }
492    
493    
494     /*
495     * Close serial port
496     */
497    
498 cebix 1.3 int16 XSERDPort::close()
499 cebix 1.1 {
500     // Kill threads
501     if (input_thread_active) {
502     quitting = true;
503     sem_post(&input_signal);
504     pthread_join(input_thread, NULL);
505     input_thread_active = false;
506     sem_destroy(&input_signal);
507     }
508     if (output_thread_active) {
509     quitting = true;
510     sem_post(&output_signal);
511     pthread_join(output_thread, NULL);
512     output_thread_active = false;
513     sem_destroy(&output_signal);
514     }
515    
516     // Close port
517     if (fd > 0)
518 cebix 1.4 ::close(fd);
519 cebix 1.1 fd = -1;
520     return noErr;
521     }
522    
523    
524     /*
525     * Configure serial port with MacOS config word
526     */
527    
528     bool XSERDPort::configure(uint16 config)
529     {
530     D(bug(" configure %04x\n", config));
531     if (is_parallel)
532     return true;
533    
534     // Set number of stop bits
535     switch (config & 0xc000) {
536     case stop10:
537     mode.c_cflag &= ~CSTOPB;
538     break;
539     case stop20:
540     mode.c_cflag |= CSTOPB;
541     break;
542     default:
543     return false;
544     }
545    
546     // Set parity mode
547     switch (config & 0x3000) {
548     case noParity:
549     mode.c_iflag &= ~INPCK;
550     mode.c_oflag &= ~PARENB;
551     break;
552     case oddParity:
553     mode.c_iflag |= INPCK;
554     mode.c_oflag |= PARENB;
555     mode.c_oflag |= PARODD;
556     break;
557     case evenParity:
558     mode.c_iflag |= INPCK;
559     mode.c_oflag |= PARENB;
560     mode.c_oflag &= ~PARODD;
561     break;
562     default:
563     return false;
564     }
565    
566     // Set number of data bits
567     switch (config & 0x0c00) {
568     case data5:
569     mode.c_cflag = mode.c_cflag & ~CSIZE | CS5;
570     break;
571     case data6:
572     mode.c_cflag = mode.c_cflag & ~CSIZE | CS6;
573     break;
574     case data7:
575     mode.c_cflag = mode.c_cflag & ~CSIZE | CS7;
576     break;
577     case data8:
578     mode.c_cflag = mode.c_cflag & ~CSIZE | CS8;
579     break;
580     }
581    
582     // Set baud rate
583     speed_t baud_rate;
584     switch (config & 0x03ff) {
585     case baud150: baud_rate = B150; break;
586     case baud300: baud_rate = B300; break;
587     case baud600: baud_rate = B600; break;
588     case baud1200: baud_rate = B1200; break;
589     case baud1800: baud_rate = B1800; break;
590     case baud2400: baud_rate = B2400; break;
591     case baud4800: baud_rate = B4800; break;
592     case baud9600: baud_rate = B9600; break;
593     case baud19200: baud_rate = B19200; break;
594     case baud38400: baud_rate = B38400; break;
595     case baud57600: baud_rate = B57600; break;
596     default:
597     return false;
598     }
599     cfsetispeed(&mode, baud_rate);
600     cfsetospeed(&mode, baud_rate);
601     tcsetattr(fd, TCSANOW, &mode);
602     return true;
603     }
604    
605    
606     /*
607     * Set serial handshaking
608     */
609    
610     void XSERDPort::set_handshake(uint32 s, bool with_dtr)
611     {
612     D(bug(" set_handshake %02x %02x %02x %02x %02x %02x %02x %02x\n",
613     ReadMacInt8(s + 0), ReadMacInt8(s + 1), ReadMacInt8(s + 2), ReadMacInt8(s + 3),
614     ReadMacInt8(s + 4), ReadMacInt8(s + 5), ReadMacInt8(s + 6), ReadMacInt8(s + 7)));
615     if (is_parallel)
616     return;
617    
618     if (with_dtr) {
619     if (ReadMacInt8(s + shkFCTS) || ReadMacInt8(s + shkFDTR))
620     mode.c_cflag |= CRTSCTS;
621     else
622     mode.c_cflag &= ~CRTSCTS;
623     } else {
624     if (ReadMacInt8(s + shkFCTS))
625     mode.c_cflag |= CRTSCTS;
626     else
627     mode.c_cflag &= ~CRTSCTS;
628     }
629    
630     D(bug(" %sware flow control\n", mode.c_cflag & CRTSCTS ? "hard" : "soft"));
631     tcsetattr(fd, TCSANOW, &mode);
632     }
633    
634    
635     /*
636     * Data input thread
637     */
638    
639     void *XSERDPort::input_func(void *arg)
640     {
641     XSERDPort *s = (XSERDPort *)arg;
642     while (!s->input_thread_cancel) {
643    
644     // Wait for commands
645     sem_wait(&s->input_signal);
646     if (s->quitting)
647     break;
648    
649     // Execute command
650     void *buf = Mac2HostAddr(ReadMacInt32(s->input_pb + ioBuffer));
651     uint32 length = ReadMacInt32(s->input_pb + ioReqCount);
652     D(bug("input_func waiting for %ld bytes of data...\n", length));
653     int32 actual = read(s->fd, buf, length);
654     D(bug(" %ld bytes received\n", actual));
655    
656     #if MONITOR
657     bug("Receiving serial data:\n");
658     uint8 *adr = (uint8 *)buf;
659     for (int i=0; i<actual; i++) {
660     bug("%02x ", adr[i]);
661     }
662     bug("\n");
663     #endif
664    
665     // KillIO called? Then simply return
666     if (s->io_killed) {
667    
668 cebix 1.6 WriteMacInt16(s->input_pb + ioResult, uint16(abortErr));
669 cebix 1.1 WriteMacInt32(s->input_pb + ioActCount, 0);
670     s->read_pending = s->read_done = false;
671    
672     } else {
673    
674     // Set error code
675     if (actual >= 0) {
676     WriteMacInt32(s->input_pb + ioActCount, actual);
677     WriteMacInt32(s->input_dt + serdtResult, noErr);
678     } else {
679     WriteMacInt32(s->input_pb + ioActCount, 0);
680 cebix 1.6 WriteMacInt32(s->input_dt + serdtResult, uint16(readErr));
681 cebix 1.1 }
682    
683     // Trigger serial interrupt
684     D(bug(" triggering serial interrupt\n"));
685     s->read_done = true;
686     SetInterruptFlag(INTFLAG_SERIAL);
687     TriggerInterrupt();
688     }
689     }
690     return NULL;
691     }
692    
693    
694     /*
695     * Data output thread
696     */
697    
698     void *XSERDPort::output_func(void *arg)
699     {
700     XSERDPort *s = (XSERDPort *)arg;
701     while (!s->output_thread_cancel) {
702    
703     // Wait for commands
704     sem_wait(&s->output_signal);
705     if (s->quitting)
706     break;
707    
708     // Execute command
709     void *buf = Mac2HostAddr(ReadMacInt32(s->output_pb + ioBuffer));
710     uint32 length = ReadMacInt32(s->output_pb + ioReqCount);
711     D(bug("output_func transmitting %ld bytes of data...\n", length));
712    
713     #if MONITOR
714     bug("Sending serial data:\n");
715     uint8 *adr = (uint8 *)buf;
716     for (int i=0; i<length; i++) {
717     bug("%02x ", adr[i]);
718     }
719     bug("\n");
720     #endif
721    
722     int32 actual = write(s->fd, buf, length);
723     D(bug(" %ld bytes transmitted\n", actual));
724    
725     // KillIO called? Then simply return
726     if (s->io_killed) {
727    
728 cebix 1.6 WriteMacInt16(s->output_pb + ioResult, uint16(abortErr));
729 cebix 1.1 WriteMacInt32(s->output_pb + ioActCount, 0);
730     s->write_pending = s->write_done = false;
731    
732     } else {
733    
734     // Set error code
735     if (actual >= 0) {
736     WriteMacInt32(s->output_pb + ioActCount, actual);
737     WriteMacInt32(s->output_dt + serdtResult, noErr);
738     } else {
739     WriteMacInt32(s->output_pb + ioActCount, 0);
740 cebix 1.6 WriteMacInt32(s->output_dt + serdtResult, uint16(writErr));
741 cebix 1.1 }
742    
743     // Trigger serial interrupt
744     D(bug(" triggering serial interrupt\n"));
745     s->write_done = true;
746     SetInterruptFlag(INTFLAG_SERIAL);
747     TriggerInterrupt();
748     }
749     }
750     return NULL;
751     }