ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/serial_unix.cpp
Revision: 1.7
Committed: 2001-02-02T20:52:58Z (23 years, 9 months ago) by cebix
Branch: MAIN
CVS Tags: snapshot-17022001
Changes since 1.6: +1 -1 lines
Log Message:
- bumped version number to 0.9
- updated copyright dates

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 if (sem_init(&input_signal, 0, 0) < 0)
212 goto open_error;
213 if (sem_init(&output_signal, 0, 0) < 0)
214 goto open_error;
215 input_thread_active = (pthread_create(&input_thread, &thread_attr, input_func, this) == 0);
216 output_thread_active = (pthread_create(&output_thread, &thread_attr, output_func, this) == 0);
217 if (!input_thread_active || !output_thread_active)
218 goto open_error;
219 return noErr;
220
221 open_error:
222 if (input_thread_active) {
223 input_thread_cancel = true;
224 #ifdef HAVE_PTHREAD_CANCEL
225 pthread_cancel(input_thread);
226 #endif
227 pthread_join(input_thread, NULL);
228 sem_destroy(&input_signal);
229 input_thread_active = false;
230 }
231 if (output_thread_active) {
232 output_thread_cancel = true;
233 #ifdef HAVE_PTHREAD_CANCEL
234 pthread_cancel(output_thread);
235 #endif
236 pthread_join(output_thread, NULL);
237 sem_destroy(&output_signal);
238 output_thread_active = false;
239 }
240 if (fd > 0) {
241 ::close(fd);
242 fd = -1;
243 }
244 return openErr;
245 }
246
247
248 /*
249 * Read data from port
250 */
251
252 int16 XSERDPort::prime_in(uint32 pb, uint32 dce)
253 {
254 // Send input command to input_thread
255 read_done = false;
256 read_pending = true;
257 input_pb = pb;
258 WriteMacInt32(input_dt + serdtDCE, dce);
259 sem_post(&input_signal);
260 return 1; // Command in progress
261 }
262
263
264 /*
265 * Write data to port
266 */
267
268 int16 XSERDPort::prime_out(uint32 pb, uint32 dce)
269 {
270 // Send output command to output_thread
271 write_done = false;
272 write_pending = true;
273 output_pb = pb;
274 WriteMacInt32(output_dt + serdtDCE, dce);
275 sem_post(&output_signal);
276 return 1; // Command in progress
277 }
278
279
280 /*
281 * Control calls
282 */
283
284 int16 XSERDPort::control(uint32 pb, uint32 dce, uint16 code)
285 {
286 switch (code) {
287 case 1: // KillIO
288 io_killed = true;
289 if (!is_parallel)
290 tcflush(fd, TCIOFLUSH);
291 while (read_pending || write_pending)
292 usleep(10000);
293 io_killed = false;
294 return noErr;
295
296 case kSERDConfiguration:
297 if (configure(ReadMacInt16(pb + csParam)))
298 return noErr;
299 else
300 return paramErr;
301
302 case kSERDInputBuffer:
303 return noErr; // Not supported under Unix
304
305 case kSERDSerHShake:
306 set_handshake(pb + csParam, false);
307 return noErr;
308
309 case kSERDSetBreak:
310 if (!is_parallel)
311 tcsendbreak(fd, 0);
312 return noErr;
313
314 case kSERDClearBreak:
315 return noErr;
316
317 case kSERDBaudRate: {
318 if (is_parallel)
319 return noErr;
320 uint16 rate = ReadMacInt16(pb + csParam);
321 speed_t baud_rate;
322 if (rate <= 50) {
323 rate = 50; baud_rate = B50;
324 } else if (rate <= 75) {
325 rate = 75; baud_rate = B75;
326 } else if (rate <= 110) {
327 rate = 110; baud_rate = B110;
328 } else if (rate <= 134) {
329 rate = 134; baud_rate = B134;
330 } else if (rate <= 150) {
331 rate = 150; baud_rate = B150;
332 } else if (rate <= 200) {
333 rate = 200; baud_rate = B200;
334 } else if (rate <= 300) {
335 rate = 300; baud_rate = B300;
336 } else if (rate <= 600) {
337 rate = 600; baud_rate = B600;
338 } else if (rate <= 1200) {
339 rate = 1200; baud_rate = B1200;
340 } else if (rate <= 1800) {
341 rate = 1800; baud_rate = B1800;
342 } else if (rate <= 2400) {
343 rate = 2400; baud_rate = B2400;
344 } else if (rate <= 4800) {
345 rate = 4800; baud_rate = B4800;
346 } else if (rate <= 9600) {
347 rate = 9600; baud_rate = B9600;
348 } else if (rate <= 19200) {
349 rate = 19200; baud_rate = B19200;
350 } else if (rate <= 38400) {
351 rate = 38400; baud_rate = B38400;
352 } else if (rate <= 57600) {
353 rate = 57600; baud_rate = B57600;
354 } else {
355 // Just for safety in case someone wants a rate between 57600 and 65535
356 rate = 57600; baud_rate = B57600;
357 }
358 WriteMacInt16(pb + csParam, rate);
359 cfsetispeed(&mode, B115200);
360 cfsetospeed(&mode, B115200);
361 tcsetattr(fd, TCSANOW, &mode);
362 return noErr;
363 }
364
365 case kSERDHandshake:
366 case kSERDHandshakeRS232:
367 set_handshake(pb + csParam, true);
368 return noErr;
369
370 case kSERDMiscOptions:
371 if (is_parallel)
372 return noErr;
373 if (ReadMacInt8(pb + csParam) & kOptionPreserveDTR)
374 mode.c_cflag &= ~HUPCL;
375 else
376 mode.c_cflag |= HUPCL;
377 tcsetattr(fd, TCSANOW, &mode);
378 return noErr;
379
380 case kSERDAssertDTR: {
381 if (is_parallel)
382 return noErr;
383 unsigned int status = TIOCM_DTR;
384 ioctl(fd, TIOCMBIS, &status);
385 return noErr;
386 }
387
388 case kSERDNegateDTR: {
389 if (is_parallel)
390 return noErr;
391 unsigned int status = TIOCM_DTR;
392 ioctl(fd, TIOCMBIC, &status);
393 return noErr;
394 }
395
396 case kSERDSetPEChar:
397 case kSERDSetPEAltChar:
398 return noErr; // Not supported under Unix
399
400 case kSERDResetChannel:
401 if (!is_parallel)
402 tcflush(fd, TCIOFLUSH);
403 return noErr;
404
405 case kSERDAssertRTS: {
406 if (is_parallel)
407 return noErr;
408 unsigned int status = TIOCM_RTS;
409 ioctl(fd, TIOCMBIS, &status);
410 return noErr;
411 }
412
413 case kSERDNegateRTS: {
414 if (is_parallel)
415 return noErr;
416 unsigned int status = TIOCM_RTS;
417 ioctl(fd, TIOCMBIC, &status);
418 return noErr;
419 }
420
421 case kSERD115KBaud:
422 if (is_parallel)
423 return noErr;
424 cfsetispeed(&mode, B115200);
425 cfsetospeed(&mode, B115200);
426 tcsetattr(fd, TCSANOW, &mode);
427 return noErr;
428
429 case kSERD230KBaud:
430 case kSERDSetHighSpeed:
431 if (is_parallel)
432 return noErr;
433 cfsetispeed(&mode, B230400);
434 cfsetospeed(&mode, B230400);
435 tcsetattr(fd, TCSANOW, &mode);
436 return noErr;
437
438 default:
439 printf("WARNING: SerialControl(): unimplemented control code %d\n", code);
440 return controlErr;
441 }
442 }
443
444
445 /*
446 * Status calls
447 */
448
449 int16 XSERDPort::status(uint32 pb, uint32 dce, uint16 code)
450 {
451 switch (code) {
452 case kSERDInputCount: {
453 int num;
454 ioctl(fd, FIONREAD, &num);
455 WriteMacInt32(pb + csParam, num);
456 return noErr;
457 }
458
459 case kSERDStatus: {
460 uint32 p = pb + csParam;
461 WriteMacInt8(p + staCumErrs, cum_errors);
462 cum_errors = 0;
463 WriteMacInt8(p + staXOffSent, 0);
464 WriteMacInt8(p + staXOffHold, 0);
465 WriteMacInt8(p + staRdPend, read_pending);
466 WriteMacInt8(p + staWrPend, write_pending);
467 if (is_parallel) {
468 WriteMacInt8(p + staCtsHold, 0);
469 WriteMacInt8(p + staDsrHold, 0);
470 WriteMacInt8(p + staModemStatus, dsrEvent | dcdEvent | ctsEvent);
471 } else {
472 unsigned int status;
473 ioctl(fd, TIOCMGET, &status);
474 WriteMacInt8(p + staCtsHold, status & TIOCM_CTS ? 0 : 1);
475 WriteMacInt8(p + staDsrHold, status & TIOCM_DTR ? 0 : 1);
476 WriteMacInt8(p + staModemStatus,
477 (status & TIOCM_DSR ? dsrEvent : 0)
478 | (status & TIOCM_RI ? riEvent : 0)
479 | (status & TIOCM_CD ? dcdEvent : 0)
480 | (status & TIOCM_CTS ? ctsEvent : 0));
481 }
482 return noErr;
483 }
484
485 default:
486 printf("WARNING: SerialStatus(): unimplemented status code %d\n", code);
487 return statusErr;
488 }
489 }
490
491
492 /*
493 * Close serial port
494 */
495
496 int16 XSERDPort::close()
497 {
498 // Kill threads
499 if (input_thread_active) {
500 quitting = true;
501 sem_post(&input_signal);
502 pthread_join(input_thread, NULL);
503 input_thread_active = false;
504 sem_destroy(&input_signal);
505 }
506 if (output_thread_active) {
507 quitting = true;
508 sem_post(&output_signal);
509 pthread_join(output_thread, NULL);
510 output_thread_active = false;
511 sem_destroy(&output_signal);
512 }
513
514 // Close port
515 if (fd > 0)
516 ::close(fd);
517 fd = -1;
518 return noErr;
519 }
520
521
522 /*
523 * Configure serial port with MacOS config word
524 */
525
526 bool XSERDPort::configure(uint16 config)
527 {
528 D(bug(" configure %04x\n", config));
529 if (is_parallel)
530 return true;
531
532 // Set number of stop bits
533 switch (config & 0xc000) {
534 case stop10:
535 mode.c_cflag &= ~CSTOPB;
536 break;
537 case stop20:
538 mode.c_cflag |= CSTOPB;
539 break;
540 default:
541 return false;
542 }
543
544 // Set parity mode
545 switch (config & 0x3000) {
546 case noParity:
547 mode.c_iflag &= ~INPCK;
548 mode.c_oflag &= ~PARENB;
549 break;
550 case oddParity:
551 mode.c_iflag |= INPCK;
552 mode.c_oflag |= PARENB;
553 mode.c_oflag |= PARODD;
554 break;
555 case evenParity:
556 mode.c_iflag |= INPCK;
557 mode.c_oflag |= PARENB;
558 mode.c_oflag &= ~PARODD;
559 break;
560 default:
561 return false;
562 }
563
564 // Set number of data bits
565 switch (config & 0x0c00) {
566 case data5:
567 mode.c_cflag = mode.c_cflag & ~CSIZE | CS5;
568 break;
569 case data6:
570 mode.c_cflag = mode.c_cflag & ~CSIZE | CS6;
571 break;
572 case data7:
573 mode.c_cflag = mode.c_cflag & ~CSIZE | CS7;
574 break;
575 case data8:
576 mode.c_cflag = mode.c_cflag & ~CSIZE | CS8;
577 break;
578 }
579
580 // Set baud rate
581 speed_t baud_rate;
582 switch (config & 0x03ff) {
583 case baud150: baud_rate = B150; break;
584 case baud300: baud_rate = B300; break;
585 case baud600: baud_rate = B600; break;
586 case baud1200: baud_rate = B1200; break;
587 case baud1800: baud_rate = B1800; break;
588 case baud2400: baud_rate = B2400; break;
589 case baud4800: baud_rate = B4800; break;
590 case baud9600: baud_rate = B9600; break;
591 case baud19200: baud_rate = B19200; break;
592 case baud38400: baud_rate = B38400; break;
593 case baud57600: baud_rate = B57600; break;
594 default:
595 return false;
596 }
597 cfsetispeed(&mode, baud_rate);
598 cfsetospeed(&mode, baud_rate);
599 tcsetattr(fd, TCSANOW, &mode);
600 return true;
601 }
602
603
604 /*
605 * Set serial handshaking
606 */
607
608 void XSERDPort::set_handshake(uint32 s, bool with_dtr)
609 {
610 D(bug(" set_handshake %02x %02x %02x %02x %02x %02x %02x %02x\n",
611 ReadMacInt8(s + 0), ReadMacInt8(s + 1), ReadMacInt8(s + 2), ReadMacInt8(s + 3),
612 ReadMacInt8(s + 4), ReadMacInt8(s + 5), ReadMacInt8(s + 6), ReadMacInt8(s + 7)));
613 if (is_parallel)
614 return;
615
616 if (with_dtr) {
617 if (ReadMacInt8(s + shkFCTS) || ReadMacInt8(s + shkFDTR))
618 mode.c_cflag |= CRTSCTS;
619 else
620 mode.c_cflag &= ~CRTSCTS;
621 } else {
622 if (ReadMacInt8(s + shkFCTS))
623 mode.c_cflag |= CRTSCTS;
624 else
625 mode.c_cflag &= ~CRTSCTS;
626 }
627
628 D(bug(" %sware flow control\n", mode.c_cflag & CRTSCTS ? "hard" : "soft"));
629 tcsetattr(fd, TCSANOW, &mode);
630 }
631
632
633 /*
634 * Data input thread
635 */
636
637 void *XSERDPort::input_func(void *arg)
638 {
639 XSERDPort *s = (XSERDPort *)arg;
640 while (!s->input_thread_cancel) {
641
642 // Wait for commands
643 sem_wait(&s->input_signal);
644 if (s->quitting)
645 break;
646
647 // Execute command
648 void *buf = Mac2HostAddr(ReadMacInt32(s->input_pb + ioBuffer));
649 uint32 length = ReadMacInt32(s->input_pb + ioReqCount);
650 D(bug("input_func waiting for %ld bytes of data...\n", length));
651 int32 actual = read(s->fd, buf, length);
652 D(bug(" %ld bytes received\n", actual));
653
654 #if MONITOR
655 bug("Receiving serial data:\n");
656 uint8 *adr = (uint8 *)buf;
657 for (int i=0; i<actual; i++) {
658 bug("%02x ", adr[i]);
659 }
660 bug("\n");
661 #endif
662
663 // KillIO called? Then simply return
664 if (s->io_killed) {
665
666 WriteMacInt16(s->input_pb + ioResult, uint16(abortErr));
667 WriteMacInt32(s->input_pb + ioActCount, 0);
668 s->read_pending = s->read_done = false;
669
670 } else {
671
672 // Set error code
673 if (actual >= 0) {
674 WriteMacInt32(s->input_pb + ioActCount, actual);
675 WriteMacInt32(s->input_dt + serdtResult, noErr);
676 } else {
677 WriteMacInt32(s->input_pb + ioActCount, 0);
678 WriteMacInt32(s->input_dt + serdtResult, uint16(readErr));
679 }
680
681 // Trigger serial interrupt
682 D(bug(" triggering serial interrupt\n"));
683 s->read_done = true;
684 SetInterruptFlag(INTFLAG_SERIAL);
685 TriggerInterrupt();
686 }
687 }
688 return NULL;
689 }
690
691
692 /*
693 * Data output thread
694 */
695
696 void *XSERDPort::output_func(void *arg)
697 {
698 XSERDPort *s = (XSERDPort *)arg;
699 while (!s->output_thread_cancel) {
700
701 // Wait for commands
702 sem_wait(&s->output_signal);
703 if (s->quitting)
704 break;
705
706 // Execute command
707 void *buf = Mac2HostAddr(ReadMacInt32(s->output_pb + ioBuffer));
708 uint32 length = ReadMacInt32(s->output_pb + ioReqCount);
709 D(bug("output_func transmitting %ld bytes of data...\n", length));
710
711 #if MONITOR
712 bug("Sending serial data:\n");
713 uint8 *adr = (uint8 *)buf;
714 for (int i=0; i<length; i++) {
715 bug("%02x ", adr[i]);
716 }
717 bug("\n");
718 #endif
719
720 int32 actual = write(s->fd, buf, length);
721 D(bug(" %ld bytes transmitted\n", actual));
722
723 // KillIO called? Then simply return
724 if (s->io_killed) {
725
726 WriteMacInt16(s->output_pb + ioResult, uint16(abortErr));
727 WriteMacInt32(s->output_pb + ioActCount, 0);
728 s->write_pending = s->write_done = false;
729
730 } else {
731
732 // Set error code
733 if (actual >= 0) {
734 WriteMacInt32(s->output_pb + ioActCount, actual);
735 WriteMacInt32(s->output_dt + serdtResult, noErr);
736 } else {
737 WriteMacInt32(s->output_pb + ioActCount, 0);
738 WriteMacInt32(s->output_dt + serdtResult, uint16(writErr));
739 }
740
741 // Trigger serial interrupt
742 D(bug(" triggering serial interrupt\n"));
743 s->write_done = true;
744 SetInterruptFlag(INTFLAG_SERIAL);
745 TriggerInterrupt();
746 }
747 }
748 return NULL;
749 }