ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Windows/serial_windows.cpp
Revision: 1.2
Committed: 2008-01-01T09:40:34Z (16 years, 4 months ago) by gbeauche
Branch: MAIN
CVS Tags: HEAD
Changes since 1.1: +1 -1 lines
Log Message:
Happy New Year!

File Contents

# Content
1 /*
2 * serial_windows.cpp - Serial device driver for Win32
3 *
4 * Basilisk II (C) 1997-2008 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 }