ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/serial_unix.cpp
Revision: 1.8
Committed: 2001-04-08T12:21:46Z (23 years, 3 months ago) by cebix
Branch: MAIN
CVS Tags: snapshot-29052001, release-0_9-1
Changes since 1.7: +2 -0 lines
Log Message:
serial_unix.cpp: input/output_thread_cancel weren't initialized [Kolja Waschk]

File Contents

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