ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/serial_unix.cpp
Revision: 1.11
Committed: 2002-03-20T19:03:40Z (22 years, 5 months ago) by cebix
Branch: MAIN
Changes since 1.10: +1 -0 lines
Log Message:
added missing return values

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