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, 5 months ago) by cebix
Branch: MAIN
Changes since 1.9: +1 -10 lines
Log Message:
cleaned up pthread attributes [Brian Johnson]

File Contents

# Content
1 /*
2 * serial_unix.cpp - Serial device driver, Unix specific stuff
3 *
4 * Basilisk II (C) 1997-2002 Christian Bauer
5 *
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 Set_pthread_attr(&thread_attr, 2);
71 }
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 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
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 int16 XSERDPort::open(uint16 config)
161 {
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 fd = ::open(device_name, O_RDWR);
172 if (fd < 0)
173 goto open_error;
174
175 #if defined(__linux__)
176 // 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 #elif defined(__FreeBSD__) || defined(__NetBSD__)
182 // 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 input_thread_cancel = false;
203 output_thread_cancel = false;
204 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 ::close(fd);
235 fd = -1;
236 }
237 return openErr;
238 }
239
240
241 /*
242 * Read data from port
243 */
244
245 int16 XSERDPort::prime_in(uint32 pb, uint32 dce)
246 {
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 int16 XSERDPort::prime_out(uint32 pb, uint32 dce)
262 {
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 int16 XSERDPort::control(uint32 pb, uint32 dce, uint16 code)
278 {
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 int16 XSERDPort::status(uint32 pb, uint32 dce, uint16 code)
443 {
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 int16 XSERDPort::close()
490 {
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 ::close(fd);
510 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 WriteMacInt16(s->input_pb + ioResult, uint16(abortErr));
660 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 WriteMacInt32(s->input_dt + serdtResult, uint16(readErr));
672 }
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 WriteMacInt16(s->output_pb + ioResult, uint16(abortErr));
720 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 WriteMacInt32(s->output_dt + serdtResult, uint16(writErr));
732 }
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 }