ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/serial_unix.cpp
Revision: 1.10
Committed: 2002-02-07T16:10:55Z (22 years, 7 months ago) by cebix
Branch: MAIN
Changes since 1.9: +1 -10 lines
Log Message:
cleaned up pthread attributes [Brian Johnson]

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