ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Windows/serial_windows.cpp
Revision: 1.1
Committed: 2005-06-20T06:11:28Z (19 years, 4 months ago) by gbeauche
Branch: MAIN
CVS Tags: nigel-build-19, nigel-build-17
Log Message:
merge in serial ports emulation from original B2/win port

File Contents

# User Rev Content
1 gbeauche 1.1 /*
2     * serial_windows.cpp - Serial device driver for Win32
3     *
4     * Basilisk II (C) 1997-1999 Christian Bauer
5     *
6     * Windows platform specific code copyright (C) Lauri Pesonen
7     *
8     * This program is free software; you can redistribute it and/or modify
9     * it under the terms of the GNU General Public License as published by
10     * the Free Software Foundation; either version 2 of the License, or
11     * (at your option) any later version.
12     *
13     * This program is distributed in the hope that it will be useful,
14     * but WITHOUT ANY WARRANTY; without even the implied warranty of
15     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16     * GNU General Public License for more details.
17     *
18     * You should have received a copy of the GNU General Public License
19     * along with this program; if not, write to the Free Software
20     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21     */
22    
23     // TODO: serial i/o threads should have high priority.
24     #include "sysdeps.h"
25    
26     #include <ctype.h>
27     #include <process.h>
28    
29     #include "main.h"
30     #include "macos_util.h"
31     #include "prefs.h"
32     #include "serial.h"
33     #include "serial_defs.h"
34     #include "cpu_emulation.h"
35    
36     // This must be always on.
37     #define DEBUG 1
38     #undef OutputDebugString
39     #define OutputDebugString serial_log_write
40     static void serial_log_write( char *s );
41     #define SERIAL_LOG_FILE_NAME "serial.log"
42     #include "debug.h"
43     #undef D
44     #define D(x) if(debug_serial != DB_SERIAL_NONE) (x);
45    
46    
47     enum {
48     DB_SERIAL_NONE=0,
49     DB_SERIAL_NORMAL,
50     DB_SERIAL_LOUD
51     };
52    
53     static int16 debug_serial = DB_SERIAL_NONE;
54    
55     static HANDLE serial_log_file = INVALID_HANDLE_VALUE;
56    
57     static void serial_log_open( char *path )
58     {
59     if(debug_serial == DB_SERIAL_NONE) return;
60    
61     DeleteFile( path );
62     serial_log_file = CreateFile(
63     path,
64     GENERIC_READ|GENERIC_WRITE,
65     FILE_SHARE_READ,
66     NULL,
67     CREATE_ALWAYS,
68     FILE_FLAG_WRITE_THROUGH,
69     NULL
70     );
71     if( serial_log_file == INVALID_HANDLE_VALUE ) {
72     ErrorAlert( "Could not create the serial log file." );
73     }
74     }
75    
76     static void serial_log_close( void )
77     {
78     if(debug_serial == DB_SERIAL_NONE) return;
79    
80     if( serial_log_file != INVALID_HANDLE_VALUE ) {
81     CloseHandle( serial_log_file );
82     serial_log_file = INVALID_HANDLE_VALUE;
83     }
84     }
85    
86     static void serial_log_write( char *s )
87     {
88     DWORD bytes_written;
89    
90     // should have been checked already.
91     if(debug_serial == DB_SERIAL_NONE) return;
92    
93     if( serial_log_file != INVALID_HANDLE_VALUE ) {
94    
95     DWORD count = strlen(s);
96     if (0 == WriteFile(serial_log_file, s, count, &bytes_written, NULL) ||
97     (int)bytes_written != count)
98     {
99     serial_log_close();
100     ErrorAlert( "serial log file write error (out of disk space?). Log closed." );
101     } else {
102     FlushFileBuffers( serial_log_file );
103     }
104     }
105     }
106    
107    
108     // Driver private variables
109     class XSERDPort : public SERDPort {
110     public:
111     XSERDPort(const char *dev, const char *suffix)
112     {
113     D(bug("XSERDPort constructor %s\r\n", dev));
114     // device_name = (char *)dev;
115    
116     read_pending = write_pending = false;
117    
118     if(dev)
119     strcpy( device_name, (char *)dev );
120     else
121     *device_name = 0;
122     strupr(device_name);
123     is_parallel = (strncmp(device_name,"LPT",3) == 0);
124     is_file = (strncmp(device_name,"FILE",4) == 0);
125     if(is_file) {
126     char entry_name[20];
127     wsprintf( entry_name, "portfile%s", suffix );
128     const char *path = PrefsFindString(entry_name);
129     if(path) {
130     strcpy( output_file_name, path );
131     } else {
132     strcpy( output_file_name, "C:\\B2TEMP.OUT" );
133     }
134     }
135    
136     is_serial = !is_parallel && !is_file;
137    
138     fd = INVALID_HANDLE_VALUE;
139     input_thread_active = output_thread_active = NULL;
140     }
141    
142     virtual ~XSERDPort()
143     {
144     D(bug("XSERDPort destructor \r\n"));
145     if (input_thread_active) {
146     D(bug("WARNING: brute TerminateThread(input)\r\n"));
147     TerminateThread(input_thread_active,0);
148     CloseHandle(input_signal);
149     input_thread_active = NULL;
150     }
151     if (output_thread_active) {
152     D(bug("WARNING: brute TerminateThread(output)\r\n"));
153     TerminateThread(output_thread_active,0);
154     CloseHandle(output_signal);
155     output_thread_active = NULL;
156     }
157     }
158    
159     virtual int16 open(uint16 config);
160     virtual int16 prime_in(uint32 pb, uint32 dce);
161     virtual int16 prime_out(uint32 pb, uint32 dce);
162     virtual int16 control(uint32 pb, uint32 dce, uint16 code);
163     virtual int16 status(uint32 pb, uint32 dce, uint16 code);
164     virtual int16 close(void);
165    
166     private:
167     bool configure(uint16 config);
168     void set_handshake(uint32 s, bool with_dtr);
169     static WINAPI unsigned int input_func(void *arg);
170     static WINAPI unsigned int output_func(void *arg);
171     static int acknowledge_error(HANDLE h, bool is_read);
172     bool set_timeouts(int bauds, int parity_bits, int stop_bits);
173    
174     char device_name[256];
175     HANDLE fd;
176    
177     bool io_killed; // Flag: KillIO called, I/O threads must not call deferred tasks
178     bool quitting; // Flag: Quit threads
179    
180     HANDLE input_thread_active; // Handle: Input thread installed (was a bool)
181     unsigned int input_thread_id;
182     HANDLE input_signal; // Signal for input thread: execute command
183     uint32 input_pb, input_dce; // Command parameters for input thread
184    
185     HANDLE output_thread_active; // Handle: Output thread installed (was a bool)
186     unsigned int output_thread_id;
187     HANDLE output_signal; // Signal for output thread: execute command
188     uint32 output_pb, output_dce; // Command parameters for output thread
189    
190     DCB mode; // Terminal configuration
191    
192     bool is_serial;
193     bool is_parallel; // true if LPTx
194    
195     bool is_file; // true if FILE
196     char output_file_name[256];
197     };
198    
199     /*
200     * Initialization
201     */
202    
203     void SerialInit(void)
204     {
205     const char *port;
206    
207     debug_serial = PrefsFindInt32("debugserial");
208    
209     serial_log_open( SERIAL_LOG_FILE_NAME );
210    
211     // Read serial preferences and create structs for both ports
212    
213     port = PrefsFindString("seriala");
214     if(port) {
215     D(bug("SerialInit seriala=%s\r\n",port));
216     }
217     the_serd_port[0] = new XSERDPort(port,"0");
218    
219     port = PrefsFindString("serialb");
220     if(port) {
221     D(bug("SerialInit serialb=%s\r\n",port));
222     }
223     the_serd_port[1] = new XSERDPort(port,"1");
224     }
225    
226    
227     /*
228     * Deinitialization
229     */
230    
231     void SerialExit(void)
232     {
233     D(bug("SerialExit\r\n"));
234     if(the_serd_port[0]) delete (XSERDPort *)the_serd_port[0];
235     if(the_serd_port[1]) delete (XSERDPort *)the_serd_port[1];
236     D(bug("SerialExit done\r\n"));
237    
238     serial_log_close();
239     }
240    
241    
242     /*
243     * Open serial port
244     */
245    
246     int16 XSERDPort::open(uint16 config)
247     {
248     // Don't open NULL name devices
249     if (!device_name || !*device_name)
250     return openErr;
251    
252     D(bug("XSERDPort::open device=%s,config=0x%X\r\n",device_name,(int)config));
253    
254     // Init variables
255     io_killed = false;
256     quitting = false;
257    
258     // Open port
259     if(is_file) {
260     DeleteFile( output_file_name );
261     fd = CreateFile( output_file_name,
262     GENERIC_READ | GENERIC_WRITE,
263     FILE_SHARE_READ,
264     NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL
265     );
266     } else {
267     fd = CreateFile( device_name, GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0 );
268     }
269     if(fd == INVALID_HANDLE_VALUE) {
270     goto open_error;
271     D(bug("XSERDPort::open failed to open port %s\r\n",device_name));
272     }
273    
274     if(is_serial) {
275     // Configure port for raw mode
276     memset( &mode, 0, sizeof(DCB) );
277     mode.DCBlength = sizeof(mode);
278     if(!GetCommState( fd, &mode ))
279     goto open_error;
280    
281     mode.fBinary = TRUE;
282     if(!configure(config)) {
283     D(bug("XSERDPort::configure failed\r\n"));
284     goto open_error;
285     }
286     }
287    
288     // Start input/output threads
289     input_signal = CreateSemaphore( 0, 0, 1, NULL);
290     if(!input_signal)
291     goto open_error;
292    
293     output_signal = CreateSemaphore( 0, 0, 1, NULL);
294     if(!output_signal)
295     goto open_error;
296    
297     D(bug("Semaphores created\r\n"));
298    
299     input_thread_active = (HANDLE)_beginthreadex( 0, 0, input_func, (LPVOID)this, 0, &input_thread_id );
300     output_thread_active = (HANDLE)_beginthreadex( 0, 0, output_func, (LPVOID)this, 0, &output_thread_id );
301    
302     if (!input_thread_active || !output_thread_active)
303     goto open_error;
304    
305     D(bug("Threads created, Open returns success\r\n"));
306     return noErr;
307    
308     open_error:
309     D(bug("Open cleanup after failure\r\n"));
310     if (input_thread_active) {
311     TerminateThread(input_thread_active,0);
312     CloseHandle(input_signal);
313     input_thread_active = false;
314     }
315     if (output_thread_active) {
316     TerminateThread(output_thread_active,0);
317     CloseHandle(output_signal);
318     output_thread_active = false;
319     }
320     if(fd != INVALID_HANDLE_VALUE) {
321     CloseHandle(fd);
322     fd = 0;
323     }
324     return openErr;
325     }
326    
327     /*
328     * Read data from port
329     */
330    
331     int16 XSERDPort::prime_in(uint32 pb, uint32 dce)
332     {
333     D(bug("XSERDPort::prime_in\r\n"));
334     // Send input command to input_thread
335     read_done = false;
336     read_pending = true;
337     input_pb = pb;
338     input_dce = dce;
339     ReleaseSemaphore(input_signal,1,NULL);
340     return 1; // Command in progress
341     }
342    
343    
344     /*
345     * Write data to port
346     */
347    
348     int16 XSERDPort::prime_out(uint32 pb, uint32 dce)
349     {
350     D(bug("XSERDPort::prime_out\r\n"));
351     // Send output command to output_thread
352     write_done = false;
353     write_pending = true;
354     output_pb = pb;
355     output_dce = dce;
356     ReleaseSemaphore(output_signal,1,NULL);
357     return 1; // Command in progress
358     }
359    
360    
361     static DWORD get_comm_output_buf_size( HANDLE h )
362     {
363     DWORD size = 0;
364     COMMPROP cp;
365    
366     if(GetCommProperties(h,&cp)) {
367     size = cp.dwCurrentTxQueue;
368     }
369     return size;
370     }
371    
372     /*
373     * Control calls
374     */
375    
376     int16 XSERDPort::control(uint32 pb, uint32 dce, uint16 code)
377     {
378     D(bug("XSERDPort::control code=%d\r\n",(int)code));
379     switch (code) {
380    
381     case kSERDClockMIDI:
382     /* http://til.info.apple.com/techinfo.nsf/artnum/n2425
383     A MIDI interface operates at 31.25 Kbaud (+/- 1%) [== 31400]
384     asynchronously, using a data format of one start bit, eight
385     data bits, and one stop bit. This makes a total of 10 bits
386     for each 320 microsecond period per serial byte.
387     */
388     D(bug("kSERDClockMIDI setting 38400,n,8,1\n"));
389     return noErr;
390    
391     /*
392     mode.BaudRate = 38400;
393     mode.ByteSize = 8;
394     mode.StopBits = ONESTOPBIT;
395     mode.Parity = NOPARITY;
396     if(!SetCommState( fd, &mode )) {
397     D(bug("kSERDClockMIDI SetCommState() failed\n"));
398     return controlErr;
399     } else {
400     if(!set_timeouts(38400,0,2)) {
401     D(bug("kSERDClockMIDI set_timeouts() failed\n"));
402     return controlErr;
403     }
404     D(bug("kSERDClockMIDI OK\n"));
405     return noErr;
406     }
407     */
408    
409     case 1: // KillIO
410     io_killed = true;
411    
412     if(is_serial) {
413     // Make sure we won't hang waiting. There is something wrong
414     // in how read_pending & write_pending are handled.
415     DWORD endtime = GetTickCount() + 1000;
416     while ( (read_pending || write_pending) && (GetTickCount() < endtime) ) {
417     Sleep(20);
418     }
419     if(read_pending || write_pending) {
420     D(bug("Warning (KillIO): read_pending=%d, write_pending=%d\n", read_pending, write_pending));
421     read_pending = write_pending = false;
422     }
423     // | PURGE_TXABORT | PURGE_RXABORT not needed, no overlapped i/o
424     PurgeComm(fd,PURGE_TXCLEAR|PURGE_RXCLEAR);
425     FlushFileBuffers(fd);
426     }
427     io_killed = false;
428     D(bug("KillIO done\n"));
429     return noErr;
430    
431     case kSERDConfiguration:
432     if (configure((uint16)ReadMacInt16(pb + csParam)))
433     return noErr;
434     else
435     return paramErr;
436    
437     case kSERDInputBuffer:
438     if(is_serial) {
439    
440     // SetupComm() wants both values, so we need to know the output size.
441     DWORD osize = get_comm_output_buf_size(fd);
442    
443     DWORD isize = ReadMacInt16(pb + csParam + 4) & 0xffffffc0;
444    
445     // 1k minimum
446     // Was this something Amiga specific -- do I need to do this?
447     if (isize < 1024)
448     isize = 1024;
449    
450     if(isize > 0 && osize > 0) {
451     if(SetupComm( fd, isize, osize )) {
452     D(bug(" buffer size is now %08lx\n", isize));
453     return noErr;
454     } else {
455     D(bug(" SetupComm(%d,%d) failed, error = %08lx\n", isize, osize, GetLastError()));
456     }
457     }
458     }
459     // Always return ok.
460     return noErr;
461    
462     case kSERDSerHShake:
463     set_handshake(pb + csParam, false);
464     return noErr;
465    
466     case kSERDSetBreak:
467     if(is_serial) {
468     if(!SetCommBreak(fd)) return controlErr;
469     }
470     return noErr;
471    
472     case kSERDClearBreak:
473     if(is_serial) {
474     if(!ClearCommBreak(fd)) return controlErr;
475     }
476     return noErr;
477    
478     case kSERDBaudRate: {
479     if (is_serial) {
480     uint16 rate = (uint16)ReadMacInt16(pb + csParam);
481     int baud_rate;
482     if (rate <= 50) {
483     rate = 50; baud_rate = CBR_110;
484     } else if (rate <= 75) {
485     rate = 75; baud_rate = CBR_110;
486     } else if (rate <= 110) {
487     rate = 110; baud_rate = CBR_110;
488     } else if (rate <= 134) {
489     rate = 134; baud_rate = CBR_110;
490     } else if (rate <= 150) {
491     rate = 150; baud_rate = CBR_110;
492     } else if (rate <= 200) {
493     rate = 200; baud_rate = CBR_300;
494     } else if (rate <= 300) {
495     rate = 300; baud_rate = CBR_300;
496     } else if (rate <= 600) {
497     rate = 600; baud_rate = CBR_600;
498     } else if (rate <= 1200) {
499     rate = 1200; baud_rate = CBR_1200;
500     } else if (rate <= 1800) {
501     rate = 1800; baud_rate = CBR_2400;
502     } else if (rate <= 2400) {
503     rate = 2400; baud_rate = CBR_2400;
504     } else if (rate <= 4800) {
505     rate = 4800; baud_rate = CBR_4800;
506     } else if (rate <= 9600) {
507     rate = 9600; baud_rate = CBR_9600;
508     } else if (rate <= 19200) {
509     rate = 19200; baud_rate = CBR_19200;
510     } else if (rate <= 38400) {
511     rate = 38400; baud_rate = CBR_38400;
512     } else if (rate <= 57600) {
513     rate = 57600; baud_rate = CBR_57600;
514     } else {
515     rate = 57600; baud_rate = CBR_57600;
516     }
517     WriteMacInt16(pb + csParam, rate);
518     mode.BaudRate = baud_rate;
519     if(!SetCommState( fd, &mode )) return controlErr;
520     // TODO: save parity/stop values and use here (not critical)
521     if(!set_timeouts(rate,0,1)) return controlErr;
522     }
523     return noErr;
524     }
525    
526     case kSERDHandshake:
527     case kSERDHandshakeRS232:
528     set_handshake(pb + csParam, true);
529     return noErr;
530    
531     case kSERDMiscOptions:
532     if (ReadMacInt8(pb + csParam) & kOptionPreserveDTR)
533     mode.fDtrControl = DTR_CONTROL_ENABLE; // correct?
534     else
535     mode.fDtrControl = DTR_CONTROL_DISABLE; // correct?
536     if(is_serial) {
537     if(!SetCommState( fd, &mode )) return controlErr;
538     }
539     return noErr;
540    
541     case kSERDAssertDTR: {
542     if (is_serial) {
543     if(!EscapeCommFunction(fd,SETDTR)) return controlErr;
544     }
545     return noErr;
546     }
547    
548     case kSERDNegateDTR: {
549     if (is_serial) {
550     if(!EscapeCommFunction(fd,CLRDTR)) return controlErr;
551     }
552     return noErr;
553     }
554    
555     case kSERDSetPEChar:
556     case kSERDSetPEAltChar:
557     {
558     uint16 errChar = (uint16)ReadMacInt16(pb + csParam);
559     mode.fErrorChar = TRUE;
560     mode.ErrorChar = (char)errChar;
561     return noErr;
562     }
563    
564     case kSERDResetChannel:
565     if (is_serial) {
566     // | PURGE_TXABORT | PURGE_RXABORT not needed, no overlapped i/o
567     PurgeComm(fd,PURGE_TXCLEAR|PURGE_RXCLEAR);
568     FlushFileBuffers(fd);
569     }
570     return noErr;
571    
572     case kSERDAssertRTS: {
573     if (is_serial) {
574     if(!EscapeCommFunction(fd,SETRTS)) return controlErr;
575     }
576     return noErr;
577     }
578    
579     case kSERDNegateRTS: {
580     if (is_serial) {
581     if(!EscapeCommFunction(fd,CLRRTS)) return controlErr;
582     }
583     return noErr;
584     }
585    
586     case kSERD115KBaud:
587     if (is_serial) {
588     mode.BaudRate = CBR_115200;
589     if(!SetCommState( fd, &mode )) return controlErr;
590     }
591     return noErr;
592    
593     case kSERD230KBaud:
594     case kSERDSetHighSpeed:
595     if (is_serial) {
596     mode.BaudRate = CBR_256000;
597     if(!SetCommState( fd, &mode )) return controlErr;
598     }
599     return noErr;
600    
601     default:
602     D(bug("WARNING: SerialControl(): unimplemented control code %d\r\n", code));
603     return controlErr;
604     }
605     }
606    
607     /*
608     * Status calls
609     */
610    
611     int16 XSERDPort::status(uint32 pb, uint32 dce, uint16 code)
612     {
613     // D(bug("XSERDPort::status code=%d\r\n",(int)code));
614    
615     DWORD error_state;
616     COMSTAT comstat;
617    
618     switch (code) {
619     case kSERDInputCount: {
620     uint32 num = 0;
621     if (is_serial) {
622     if(!ClearCommError(fd,&error_state,&comstat)) return statusErr;
623     num = comstat.cbInQue;
624     }
625     WriteMacInt32(pb + csParam, num);
626     return noErr;
627     }
628    
629     case kSERDStatus: {
630     uint32 p = pb + csParam;
631     WriteMacInt8(p + staCumErrs, cum_errors);
632     cum_errors = 0;
633     DWORD status;
634    
635     if(is_serial) {
636     if(!GetCommModemStatus(fd,&status)) return statusErr;
637     } else {
638     status = MS_CTS_ON | MS_DSR_ON | MS_RLSD_ON;
639     D(bug("kSERDStatus: faking status for LPT port or FILE\r\n"));
640     }
641    
642     WriteMacInt8(p + staXOffSent, 0);
643     WriteMacInt8(p + staXOffHold, 0);
644     WriteMacInt8(p + staRdPend, read_pending);
645     WriteMacInt8(p + staWrPend, write_pending);
646    
647     WriteMacInt8(p + staCtsHold, status & MS_CTS_ON ? 0 : 1);
648     WriteMacInt8(p + staDsrHold, status & MS_DSR_ON ? 0 : 1);
649    
650     WriteMacInt8(p + staModemStatus,
651     (status & MS_DSR_ON ? dsrEvent : 0)
652     | (status & MS_RING_ON ? riEvent : 0)
653     | (status & MS_RLSD_ON ? dcdEvent : 0) // is this carrier detect?
654     | (status & MS_CTS_ON ? ctsEvent : 0));
655     return noErr;
656     }
657    
658     default:
659     D(bug("WARNING: SerialStatus(): unimplemented status code %d\r\n", code));
660     return statusErr;
661     }
662     }
663    
664    
665     /*
666     * Close serial port
667     */
668    
669     int16 XSERDPort::close()
670     {
671     D(bug("XSERDPort::close\r\n"));
672    
673     // Kill threads
674     if (input_thread_active) {
675     quitting = true;
676     ReleaseSemaphore(input_signal,1,NULL);
677     input_thread_active = false;
678     CloseHandle(input_signal);
679     }
680     if (output_thread_active) {
681     quitting = true;
682     ReleaseSemaphore(output_signal,1,NULL);
683     output_thread_active = false;
684     // bugfix: was: CloseHandle(&output_signal);
685     CloseHandle(output_signal);
686     }
687    
688     // Close port
689     if(fd != INVALID_HANDLE_VALUE) {
690     CloseHandle(fd);
691     fd = 0;
692     }
693     return noErr;
694     }
695    
696     bool XSERDPort::set_timeouts(
697     int bauds, int parity_bits, int stop_bits )
698     {
699     COMMTIMEOUTS timeouts;
700     uint32 bytes_per_sec;
701     uint32 msecs_per_ch;
702     bool result = false;
703    
704     // Should already been checked
705     if (!is_serial)
706     return true;
707    
708     bytes_per_sec = bauds / (mode.ByteSize + parity_bits + stop_bits);
709    
710     // 75% bytes_per_sec
711     // bytes_per_sec = (bytes_per_sec+bytes_per_sec+bytes_per_sec) >> 2;
712    
713     // 50% bytes_per_sec
714     bytes_per_sec = bytes_per_sec >> 1;
715    
716     msecs_per_ch = 1000 / bytes_per_sec;
717     if(msecs_per_ch == 0) msecs_per_ch = 1;
718    
719     if(GetCommTimeouts(fd,&timeouts)) {
720     D(bug("old timeout values: %ld %ld %ld %ld %ld\r\n",
721     timeouts.ReadIntervalTimeout,
722     timeouts.ReadTotalTimeoutMultiplier,
723     timeouts.ReadTotalTimeoutConstant,
724     timeouts.WriteTotalTimeoutMultiplier,
725     timeouts.WriteTotalTimeoutConstant
726     ));
727    
728     timeouts.WriteTotalTimeoutMultiplier = msecs_per_ch;
729     timeouts.WriteTotalTimeoutConstant = 10;
730    
731     /*
732     timeouts.ReadIntervalTimeout = msecs_per_ch;
733     timeouts.ReadTotalTimeoutMultiplier = msecs_per_ch;
734     timeouts.ReadTotalTimeoutConstant = 10;
735     */
736    
737     timeouts.ReadIntervalTimeout = MAXDWORD;
738     timeouts.ReadTotalTimeoutMultiplier = 0;
739     timeouts.ReadTotalTimeoutConstant = 0;
740    
741     if(!SetCommTimeouts(fd,&timeouts)) {
742     D(bug("SetCommTimeouts() failed in configure()\r\n"));
743     } else {
744     D(bug("new timeout values: %ld %ld %ld %ld %ld\r\n",
745     timeouts.ReadIntervalTimeout,
746     timeouts.ReadTotalTimeoutMultiplier,
747     timeouts.ReadTotalTimeoutConstant,
748     timeouts.WriteTotalTimeoutMultiplier,
749     timeouts.WriteTotalTimeoutConstant
750     ));
751     result = true;
752     }
753     } else {
754     D(bug("GetCommTimeouts() failed in set_timeouts()\r\n"));
755     }
756     return(result);
757     }
758    
759     /*
760     * Configure serial port with MacOS config word
761     */
762    
763     bool XSERDPort::configure(uint16 config)
764     {
765     D(bug("XSERDPort::configure, config=%d\r\n",(int)config));
766    
767     if (!is_serial)
768     return true;
769    
770     // needed to calculate optimal timeouts
771     uint32 bauds = 57600;
772     uint32 stop_bits = 1;
773     uint32 parity_bits = 0;
774    
775     // Not all of these can be set here anyway.
776     /*
777     mode.fOutxCtsFlow = TRUE;
778     mode.fOutxDsrFlow = FALSE;
779     mode.fDtrControl = DTR_CONTROL_ENABLE; // DTR_CONTROL_HANDSHAKE?
780     mode.fDsrSensitivity = FALSE; // ???
781     mode.fOutX = FALSE;
782     mode.fInX = FALSE;
783     mode.fTXContinueOnXoff = FALSE;
784     mode.fErrorChar = FALSE;
785     mode.ErrorChar = 0;
786     mode.fNull = FALSE;
787     mode.fRtsControl = 2; // ???
788     mode.fAbortOnError = FALSE;
789     mode.XonLim = 0x800;
790     mode.XoffLim = 0x200;
791     mode.XonChar = 0x11;
792     mode.XoffChar = 0x13;
793     mode.EofChar = 0;
794     mode.EvtChar = '\0';
795     */
796    
797     // Set baud rate
798     switch (config & 0x03ff) {
799     // no baud1800, CBR_14400, CBR_56000, CBR_115200, CBR_128000, CBR_256000
800     case baud150: mode.BaudRate = CBR_110; bauds = 110; break;
801     case baud300: mode.BaudRate = CBR_300; bauds = 300; break;
802     case baud600: mode.BaudRate = CBR_600; bauds = 600; break;
803     case baud1200: mode.BaudRate = CBR_1200; bauds = 1200; break;
804     case baud1800: return false;
805     case baud2400: mode.BaudRate = CBR_2400; bauds = 2400; break;
806     case baud4800: mode.BaudRate = CBR_4800; bauds = 4800; break;
807     case baud9600: mode.BaudRate = CBR_9600; bauds = 9600; break;
808     case baud19200: mode.BaudRate = CBR_19200; bauds = 19200; break;
809     case baud38400: mode.BaudRate = CBR_38400; bauds = 38400; break;
810     case baud57600: mode.BaudRate = CBR_57600; bauds = 57600; break;
811     default:
812     return false;
813     }
814    
815     // Set number of stop bits
816     switch (config & 0xc000) {
817     case stop10:
818     mode.StopBits = ONESTOPBIT;
819     stop_bits = 1;
820     break;
821     case stop15:
822     mode.StopBits = ONE5STOPBITS;
823     stop_bits = 2;
824     break;
825     case stop20:
826     mode.StopBits = TWOSTOPBITS;
827     stop_bits = 2;
828     break;
829     default:
830     return false;
831     }
832    
833     // Set parity mode
834     switch (config & 0x3000) {
835     case noParity:
836     mode.Parity = NOPARITY;
837     mode.fParity = FALSE;
838     parity_bits = 0;
839     break;
840     case oddParity:
841     mode.Parity = ODDPARITY;
842     mode.fParity = TRUE;
843     parity_bits = 1;
844     break;
845     case evenParity:
846     mode.Parity = EVENPARITY;
847     mode.fParity = TRUE;
848     parity_bits = 1;
849     break;
850     // No MARKPARITY, SPACEPARITY
851     default:
852     return false;
853     }
854    
855     // Set number of data bits
856     switch (config & 0x0c00) {
857     // No data4
858     case data5:
859     mode.ByteSize = 5; break;
860     case data6:
861     mode.ByteSize = 6; break;
862     case data7:
863     mode.ByteSize = 7; break;
864     case data8:
865     mode.ByteSize = 8; break;
866     default:
867     return false;
868     }
869    
870     D(bug("Interpreted configuration: %d,%d,%d,%d\r\n",
871     bauds,
872     mode.ByteSize,
873     stop_bits,
874     parity_bits
875     ));
876    
877     if(!SetCommState( fd, &mode )) {
878     D(bug("SetCommState failed in configure()\r\n"));
879     return false;
880     }
881    
882     if(!set_timeouts(bauds,parity_bits,stop_bits))
883     return false;
884    
885     return true;
886     }
887    
888    
889     /*
890     * Set serial handshaking
891     */
892    
893     void XSERDPort::set_handshake(uint32 s, bool with_dtr)
894     {
895     D(bug(" set_handshake %02x %02x %02x %02x %02x %02x %02x %02x\r\n",
896     ReadMacInt8(s + 0), ReadMacInt8(s + 1), ReadMacInt8(s + 2), ReadMacInt8(s + 3),
897     ReadMacInt8(s + 4), ReadMacInt8(s + 5), ReadMacInt8(s + 6), ReadMacInt8(s + 7)));
898    
899     if (!is_serial)
900     return;
901    
902     if (with_dtr) {
903     mode.fDtrControl = DTR_CONTROL_ENABLE;
904     if (ReadMacInt8(s + shkFCTS) || ReadMacInt8(s + shkFDTR))
905     mode.fOutxCtsFlow = TRUE;
906     else
907     mode.fOutxCtsFlow = FALSE;
908     } else {
909     mode.fDtrControl = DTR_CONTROL_DISABLE;
910     if (ReadMacInt8(s + shkFCTS))
911     mode.fOutxCtsFlow = TRUE;
912     else
913     mode.fOutxCtsFlow = FALSE;
914     }
915    
916     // MIDI: set_handshake 00 00 f4 f5 21 00 00 00
917     // shkFXOn = 0
918     // shkFCTS = 0
919     // shkXOn = f4
920     // shkXOff = f5
921     // shkErrs = 21
922     // shkEvts = 0
923     // shkFInX = 0
924     // shkFDTR = 0
925     if (ReadMacInt8(s + shkXOn) && ReadMacInt8(s + shkXOn)) {
926     mode.fOutX = 1;
927     mode.fInX = 1;
928     mode.XonChar = ReadMacInt8(s + shkXOn);
929     mode.XoffChar = ReadMacInt8(s + shkXOff);
930     } else {
931     mode.fOutX = 0;
932     mode.fInX = 0;
933     }
934     if (ReadMacInt8(s + shkErrs)) {
935     mode.ErrorChar = ReadMacInt8(s + shkErrs);
936     mode.fErrorChar = 1;
937     } else {
938     mode.fErrorChar = 0;
939     }
940    
941     (void)SetCommState( fd, &mode );
942    
943     // D(bug(" %sware flow control\r\n", mode.c_cflag & CRTSCTS ? "hard" : "soft"));
944     // tcsetattr(fd, TCSANOW, &mode);
945     }
946    
947     /*
948     if mode.fAbortOnError is TRUE, ClearCommError() *MUST* be called
949     after any read or write errors. Otherwise no i/o will occur again
950    
951     These error codes should be translated but the Mac Device Manager
952     error code mnemonics are too cryptic to me.
953     */
954    
955     int XSERDPort::acknowledge_error(HANDLE h, bool is_read)
956     {
957     DWORD error_state;
958     COMSTAT comstat;
959     int err;
960    
961     // default error code if cannot map correctly
962     err = is_read ? readErr : writErr;
963    
964     if(ClearCommError(h,&error_state,&comstat)) {
965     D(bug("A %s error 0x%X occured.\r\n", is_read ? "read" : "write", error_state));
966     D(bug("There was %d bytes in input buffer and %d bytes in output buffer.\r\n",(int)comstat.cbInQue,(int)comstat.cbOutQue));
967     if(error_state & CE_MODE) {
968     D(bug("The requested mode is not supported.\r\n"));
969     } else {
970     if(error_state & CE_BREAK) {
971     D(bug("The hardware detected a break condition.\r\n"));
972     }
973     if(error_state & CE_FRAME) {
974     D(bug("The hardware detected a framing error.\r\n"));
975     }
976     if(error_state & CE_IOE) {
977     D(bug("An I/O error occurred during communications with the device.\r\n"));
978     }
979     if(error_state & CE_RXOVER) {
980     D(bug("An input buffer overflow has occurred.\r\n"));
981     }
982     if(error_state & CE_RXPARITY) {
983     D(bug("The hardware detected a parity error.\r\n"));
984     err = badDCksum;
985     }
986     if(error_state & CE_TXFULL) {
987     D(bug("The application tried to transmit a character, but the output buffer was full.\r\n"));
988     }
989    
990     // Win95 specific errors
991     if(error_state & CE_OVERRUN) {
992     D(bug("A character-buffer overrun has occurred. The next character is lost.\r\n"));
993     if(!is_read) err = wrUnderrun;
994     }
995    
996     // Win95 parallel devices really.
997     if(error_state & CE_DNS) {
998     D(bug("A parallel device is not selected (Windows 95).\r\n"));
999     }
1000     if(error_state & CE_OOP) {
1001     D(bug("A parallel device signaled that it is out of paper (Windows 95 only).\r\n"));
1002     err = unitEmptyErr;
1003     }
1004     if(error_state & CE_PTO) {
1005     D(bug("A time-out occurred on a parallel device (Windows 95).\r\n"));
1006     }
1007    
1008     }
1009     } else {
1010     D(bug("Failed to resume after %s operation.\r\n",is_read ? "read" : "write"));
1011     }
1012     return(err);
1013     }
1014    
1015     #if DEBUG
1016     static void dump_dirst_bytes( BYTE *buf, int32 actual )
1017     {
1018     if(debug_serial != DB_SERIAL_LOUD) return;
1019    
1020     BYTE b[256];
1021     int32 i, bytes = min(actual,sizeof(b)-3);
1022    
1023     for (i=0; i<bytes; i++) {
1024     b[i] = isprint(buf[i]) ? buf[i] : '.';
1025     }
1026     b[i] = 0;
1027     strcat((char*)b,"\r\n");
1028     D(bug((char*)b));
1029     }
1030     #else
1031     #define dump_dirst_bytes(b,a) {}
1032     #endif
1033    
1034     /*
1035     * Data input thread
1036     */
1037    
1038     unsigned int XSERDPort::input_func(void *arg)
1039     {
1040     XSERDPort *s = (XSERDPort *)arg;
1041     int error_code;
1042    
1043     #if 0
1044     SetThreadPriority( GetCurrentThread(), threads[THREAD_SERIAL_IN].priority_running );
1045     SetThreadAffinityMask( GetCurrentThread(), threads[THREAD_SERIAL_IN].affinity_mask );
1046     set_desktop();
1047     #endif
1048    
1049     D(bug("XSERDPort::input_func started for device %s\r\n",s->device_name));
1050    
1051     for (;;) {
1052    
1053     // Wait for commands
1054     WaitForSingleObject(s->input_signal,INFINITE);
1055     if (s->quitting)
1056     break;
1057    
1058     // Execute command
1059     void *buf = Mac2HostAddr(ReadMacInt32(s->input_pb + ioBuffer));
1060     uint32 length = ReadMacInt32(s->input_pb + ioReqCount);
1061     D(bug("input_func waiting for %ld bytes of data...\r\n", length));
1062    
1063     if(length & 0xFFFF0000) {
1064     length &= 0x0000FFFF;
1065     D(bug("byte count fixed to be %ld...\r\n", length));
1066     }
1067    
1068     int32 actual;
1069     if(s->is_file) {
1070     actual = -1;
1071     error_code = readErr;
1072     } else if(!ReadFile(s->fd, buf, length, (LPDWORD)&actual, 0)) {
1073     actual = -1;
1074     if(s->is_serial)
1075     error_code = acknowledge_error(s->fd,true);
1076     else
1077     error_code = readErr;
1078     }
1079     D(bug(" %ld bytes received\r\n", actual));
1080     if(actual > 0) {
1081     dump_dirst_bytes( (BYTE*)buf, actual );
1082     }
1083    
1084     // KillIO called? Then simply return
1085     if (s->io_killed) {
1086    
1087     WriteMacInt16(s->input_pb + ioResult, abortErr);
1088     WriteMacInt32(s->input_pb + ioActCount, 0);
1089     s->read_pending = s->read_done = false;
1090    
1091     } else {
1092    
1093     // Set error code
1094     if (actual >= 0) {
1095     WriteMacInt32(s->input_pb + ioActCount, actual);
1096     WriteMacInt32(s->input_dt + serdtResult, noErr);
1097     } else {
1098     WriteMacInt32(s->input_pb + ioActCount, 0);
1099     WriteMacInt32(s->input_dt + serdtResult, error_code);
1100     }
1101    
1102     // Trigger serial interrupt
1103     D(bug(" triggering serial interrupt\r\n"));
1104     WriteMacInt32(s->input_dt + serdtDCE, s->input_dce);
1105     s->read_done = true;
1106     SetInterruptFlag(INTFLAG_SERIAL);
1107     TriggerInterrupt();
1108     }
1109     }
1110    
1111     D(bug("XSERDPort::input_func terminating gracefully\r\n"));
1112    
1113     _endthreadex( 0 );
1114    
1115     return(0);
1116     }
1117    
1118    
1119     /*
1120     * Data output thread
1121     */
1122    
1123     unsigned int XSERDPort::output_func(void *arg)
1124     {
1125     XSERDPort *s = (XSERDPort *)arg;
1126     int error_code;
1127    
1128     #if 0
1129     SetThreadPriority( GetCurrentThread(), threads[THREAD_SERIAL_OUT].priority_running );
1130     SetThreadAffinityMask( GetCurrentThread(), threads[THREAD_SERIAL_OUT].affinity_mask );
1131     set_desktop();
1132     #endif
1133    
1134     D(bug("XSERDPort::output_func started for device %s\r\n",s->device_name));
1135    
1136     for (;;) {
1137    
1138     // Wait for commands
1139     WaitForSingleObject(s->output_signal,INFINITE);
1140     if (s->quitting)
1141     break;
1142    
1143     // Execute command
1144     void *buf = Mac2HostAddr(ReadMacInt32(s->output_pb + ioBuffer));
1145     uint32 length = ReadMacInt32(s->output_pb + ioReqCount);
1146     D(bug("output_func transmitting %ld bytes of data...\r\n", length));
1147    
1148     if(length & 0xFFFF0000) {
1149     length &= 0x0000FFFF;
1150     D(bug("byte count fixed to be %ld...\r\n", length));
1151     }
1152    
1153     int32 actual;
1154     if(!WriteFile(s->fd, buf, length, (LPDWORD)&actual, 0)) {
1155     actual = -1;
1156     if(s->is_serial)
1157     error_code = acknowledge_error(s->fd,false);
1158     else
1159     error_code = writErr;
1160     }
1161     D(bug(" %ld bytes transmitted\r\n", actual));
1162     if(actual > 0) {
1163     dump_dirst_bytes( (BYTE*)buf, actual );
1164     }
1165    
1166     // KillIO called? Then simply return
1167     if (s->io_killed) {
1168    
1169     WriteMacInt16(s->output_pb + ioResult, abortErr);
1170     WriteMacInt32(s->output_pb + ioActCount, 0);
1171     s->write_pending = s->write_done = false;
1172    
1173     } else {
1174    
1175     // Set error code
1176     if (actual >= 0) {
1177     WriteMacInt32(s->output_pb + ioActCount, actual);
1178     WriteMacInt32(s->output_dt + serdtResult, noErr);
1179     } else {
1180     WriteMacInt32(s->output_pb + ioActCount, 0);
1181     WriteMacInt32(s->output_dt + serdtResult, error_code);
1182     }
1183    
1184     // Trigger serial interrupt
1185     D(bug(" triggering serial interrupt\r\n"));
1186     WriteMacInt32(s->output_dt + serdtDCE, s->output_dce);
1187     s->write_done = true;
1188     SetInterruptFlag(INTFLAG_SERIAL);
1189     TriggerInterrupt();
1190     }
1191     }
1192    
1193     D(bug("XSERDPort::output_func terminating gracefully\r\n"));
1194    
1195     _endthreadex( 0 );
1196    
1197     return(0);
1198     }