1 #include <minix/config.h>
2 /*---------------------------------------------------------------------------*
3 * rs232.c - serial driver for 8250 and 16450 UARTs *
4 * Added support for Atari ST M68901 and YM-2149 --kub *
5 *---------------------------------------------------------------------------*/
6
7 #include "../drivers.h"
8 #include <termios.h>
9 #include <signal.h>
10 #include "tty.h"
11
12 #if NR_RS_LINES > 0
13
14 #if (MACHINE != IBM_PC) && (MACHINE != ATARI)
15 #error /* rs232.c only supports PC and Atari ST */
16 #endif
17
18 #if (MACHINE == ATARI)
19 #include "staddr.h"
20 #include "stsound.h"
21 #include "stmfp.h"
22 #if (NR_RS_LINES > 1)
23 #error /* Only one physical RS232 line available */
24 #endif
25 #endif
26
27 #if (MACHINE == IBM_PC) /* PC/AT 8250/16450 chip combination */
28
29 /* 8250 constants. */
30 #define UART_FREQ 115200L /* timer frequency */
31
32 /* Interrupt enable bits. */
33 #define IE_RECEIVER_READY 1
34 #define IE_TRANSMITTER_READY 2
35 #define IE_LINE_STATUS_CHANGE 4
36 #define IE_MODEM_STATUS_CHANGE 8
37
38 /* Interrupt status bits. */
39 #define IS_MODEM_STATUS_CHANGE 0
40 #define IS_TRANSMITTER_READY 2
41 #define IS_RECEIVER_READY 4
42 #define IS_LINE_STATUS_CHANGE 6
43
44 /* Line control bits. */
45 #define LC_2STOP_BITS 0x04
46 #define LC_PARITY 0x08
47 #define LC_PAREVEN 0x10
48 #define LC_BREAK 0x40
49 #define LC_ADDRESS_DIVISOR 0x80
50
51 /* Line status bits. */
52 #define LS_OVERRUN_ERR 2
53 #define LS_PARITY_ERR 4
54 #define LS_FRAMING_ERR 8
55 #define LS_BREAK_INTERRUPT 0x10
56 #define LS_TRANSMITTER_READY 0x20
57
58 /* Modem control bits. */
59 #define MC_DTR 1
60 #define MC_RTS 2
61 #define MC_OUT2 8 /* required for PC & AT interrupts */
62
63 /* Modem status bits. */
64 #define MS_CTS 0x10
65 #define MS_RLSD 0x80 /* Received Line Signal Detect */
66 #define MS_DRLSD 0x08 /* RLSD Delta */
67
68 #else /* MACHINE == ATARI */ /* Atari ST 68901 USART */
69
70 /* Most of the USART constants are already defined in stmfp.h . The local
71 * definitions made here are for keeping C code changes smaller. --kub
72 */
73
74 #define UART_FREQ 19200L /* timer frequency */
75
76 /* Line status bits. */
77 #define LS_OVERRUN_ERR R_OE
78 #define LS_PARITY_ERR R_PE
79 #define LS_FRAMING_ERR R_FE
80 #define LS_BREAK_INTERRUPT R_BREAK
81
82 /* Modem status bits. */
83 #define MS_CTS IO_SCTS /* 0x04 */
84
85 #endif /* MACHINE == ATARI */
86
87 #define DATA_BITS_SHIFT 8 /* amount data bits shifted in mode */
88 #define DEF_BAUD 1200 /* default baud rate */
89
90 #define RS_IBUFSIZE 1024 /* RS232 input buffer size */
91 #define RS_OBUFSIZE 1024 /* RS232 output buffer size */
92
93 /* Input buffer watermarks.
94 * The external device is asked to stop sending when the buffer
95 * exactly reaches high water, or when TTY requests it. Sending restarts
96 * when the input buffer empties below the low watermark.
97 */
98 #define RS_ILOWWATER (1 * RS_IBUFSIZE / 4)
99 #define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4)
100
101 /* Output buffer low watermark.
102 * TTY is notified when the output buffer empties below the low watermark, so
103 * it may continue filling the buffer if doing a large write.
104 */
105 #define RS_OLOWWATER (1 * RS_OBUFSIZE / 4)
106
107 #if (MACHINE == IBM_PC)
108
109 /* Macros to handle flow control.
110 * Interrupts must be off when they are used.
111 * Time is critical - already the function call for outb() is annoying.
112 * If outb() can be done in-line, tests to avoid it can be dropped.
113 * istart() tells external device we are ready by raising RTS.
114 * istop() tells external device we are not ready by dropping RTS.
115 * DTR is kept high all the time (it probably should be raised by open and
116 * dropped by close of the device).
117 * OUT2 is also kept high all the time.
118 */
119 #define istart(rs) \
120 (sys_outb((rs)->modem_ctl_port, MC_OUT2 | MC_RTS | MC_DTR), \
121 (rs)->idevready = TRUE)
122 #define istop(rs) \
123 (sys_outb((rs)->modem_ctl_port, MC_OUT2 | MC_DTR), \
124 (rs)->idevready = FALSE)
125
126 /* Macro to tell if device is ready. The rs->cts field is set to MS_CTS if
127 * CLOCAL is in effect for a line without a CTS wire.
128 */
129 #define devready(rs) ((my_inb(rs->modem_status_port) | rs->cts) & MS_CTS)
130
131 /* Macro to tell if transmitter is ready. */
132 #define txready(rs) (my_inb(rs->line_status_port) & LS_TRANSMITTER_READY)
133
134 /* Macro to tell if carrier has dropped.
135 * The RS232 Carrier Detect (CD) line is usually connected to the 8250
136 * Received Line Signal Detect pin, reflected by bit MS_RLSD in the Modem
137 * Status Register. The MS_DRLSD bit tells if MS_RLSD has just changed state.
138 * So if MS_DRLSD is set and MS_RLSD cleared, we know that carrier has just
139 * dropped.
140 */
141 #define devhup(rs) \
142 ((my_inb(rs->modem_status_port) & (MS_RLSD|MS_DRLSD)) == MS_DRLSD)
143
144 #else /* MACHINE == ATARI */
145
146 /* Macros to handle flow control.
147 * Time is critical - already the function call for lock()/restore() is
148 * annoying.
149 * istart() tells external device we are ready by raising RTS.
150 * istop() tells external device we are not ready by dropping RTS.
151 * DTR is kept high all the time (it probably should be raised by open and
152 * dropped by close of the device). NOTE: The modem lines are active low.
153 */
154 #define set_porta(msk,val) { register int s = lock(); \
155 SOUND->sd_selr = YM_IOA; \
156 SOUND->sd_wdat = \
157 SOUND->sd_rdat & (msk) | (val); \
158 restore(s); }
159 #define istart(rs) { set_porta( ~(PA_SRTS|PA_SDTR),0 ); \
160 (rs)->idevready = TRUE; }
161 #define istop(rs) { set_porta( ~PA_SDTR, PA_SRTS ); \
162 (rs)->idevready = FALSE; }
163
164 /* Macro to tell if device is ready. The rs->cts field is set to MS_CTS if
165 * CLOCAL is in effect for a line without a CTS wire.
166 */
167 #define devready(rs) ((~MFP->mf_gpip | rs->cts) & MS_CTS)
168
169 /* Transmitter ready test */
170 #define txready(rs) (MFP->mf_tsr & (T_EMPTY | T_UE))
171
172 #endif /* MACHINE == ATARI */
173
174 /* Types. */
175 typedef unsigned char bool_t; /* boolean */
176
177 /* RS232 device structure, one per device. */
178 typedef struct rs232 {
179 tty_t *tty; /* associated TTY structure */
180
181 int icount; /* number of bytes in the input buffer */
182 char *ihead; /* next free spot in input buffer */
183 char *itail; /* first byte to give to TTY */
184 bool_t idevready; /* nonzero if we are ready to receive (RTS) */
185 char cts; /* normally 0, but MS_CTS if CLOCAL is set */
186
187 unsigned char ostate; /* combination of flags: */
188 #define ODONE 1 /* output completed (< output enable bits) */
189 #define ORAW 2 /* raw mode for xoff disable (< enab. bits) */
190 #define OWAKEUP 4 /* tty_wakeup() pending (asm code only) */
191 #define ODEVREADY MS_CTS /* external device hardware ready (CTS) */
192 #define OQUEUED 0x20 /* output buffer not empty */
193 #define OSWREADY 0x40 /* external device software ready (no xoff) */
194 #define ODEVHUP MS_RLSD /* external device has dropped carrier */
195 #define OSOFTBITS (ODONE | ORAW | OWAKEUP | OQUEUED | OSWREADY)
196 /* user-defined bits */
197 #if (OSOFTBITS | ODEVREADY | ODEVHUP) == OSOFTBITS
198 /* a weak sanity check */
199 #error /* bits are not unique */
200 #endif
201 unsigned char oxoff; /* char to stop output */
202 bool_t inhibited; /* output inhibited? (follows tty_inhibited) */
203 bool_t drain; /* if set drain output and reconfigure line */
204 int ocount; /* number of bytes in the output buffer */
205 char *ohead; /* next free spot in output buffer */
206 char *otail; /* next char to output */
207
208 #if (MACHINE == IBM_PC)
209 port_t xmit_port; /* i/o ports */
210 port_t recv_port;
211 port_t div_low_port;
212 port_t div_hi_port;
213 port_t int_enab_port;
214 port_t int_id_port;
215 port_t line_ctl_port;
216 port_t modem_ctl_port;
217 port_t line_status_port;
218 port_t modem_status_port;
219 #endif
220
221 unsigned char lstatus; /* last line status */
222 unsigned char pad; /* ensure alignment for 16-bit ints */
223 unsigned framing_errors; /* error counts (no reporting yet) */
224 unsigned overrun_errors;
225 unsigned parity_errors;
226 unsigned break_interrupts;
227
228 int irq; /* irq for this line */
229 int irq_hook_id; /* interrupt hook */
230
231 char ibuf[RS_IBUFSIZE]; /* input buffer */
232 char obuf[RS_OBUFSIZE]; /* output buffer */
233 } rs232_t;
234
235 PUBLIC rs232_t rs_lines[NR_RS_LINES];
236
237 /* Table and macro to translate an RS232 line number to its rs_lines entry. */
238 PRIVATE rs232_t *p_rs_addr[NR_RS_LINES];
239
240 #define rs_addr(line) (p_rs_addr[line])
241
242 #if (MACHINE == IBM_PC)
243 /* 8250 base addresses. */
244 PRIVATE port_t addr_8250[] = {
245 0x3F8, /* COM1 */
246 0x2F8, /* COM2 */
247 0x3E8, /* COM3 */
248 0x2E8, /* COM4 */
249 };
250 #endif
251
252 FORWARD _PROTOTYPE( void in_int, (rs232_t *rs) );
253 FORWARD _PROTOTYPE( void line_int, (rs232_t *rs) );
254 FORWARD _PROTOTYPE( void modem_int, (rs232_t *rs) );
255 FORWARD _PROTOTYPE( int rs_write, (tty_t *tp, int try) );
256 FORWARD _PROTOTYPE( void rs_echo, (tty_t *tp, int c) );
257 FORWARD _PROTOTYPE( int rs_ioctl, (tty_t *tp, int try) );
258 FORWARD _PROTOTYPE( void rs_config, (rs232_t *rs) );
259 FORWARD _PROTOTYPE( int rs_read, (tty_t *tp, int try) );
260 FORWARD _PROTOTYPE( int rs_icancel, (tty_t *tp, int try) );
261 FORWARD _PROTOTYPE( int rs_ocancel, (tty_t *tp, int try) );
262 FORWARD _PROTOTYPE( void rs_ostart, (rs232_t *rs) );
263 FORWARD _PROTOTYPE( int rs_break, (tty_t *tp, int try) );
264 FORWARD _PROTOTYPE( int rs_close, (tty_t *tp, int try) );
265 FORWARD _PROTOTYPE( void out_int, (rs232_t *rs) );
266 FORWARD _PROTOTYPE( void rs232_handler, (rs232_t *rs) );
267
268 /* XXX */
269 PRIVATE void lock(void) {}
270 PRIVATE void unlock(void) {}
271
272 PRIVATE int my_inb(port_t port)
273 {
274 int r, v = 0;
275 r = sys_inb(port, &v);
276 if (r != OK)
277 printf("RS232 warning: failed inb 0x%x\n", port);
278
279 return v;
280 }
281
282 /*===========================================================================*
283 * rs_write *
284 *===========================================================================*/
285 PRIVATE int rs_write(tp, try)
286 register tty_t *tp;
287 int try;
288 {
289 /* (*devwrite)() routine for RS232. */
290
291 rs232_t *rs = tp->tty_priv;
292 int count, ocount;
293
294 if (rs->inhibited != tp->tty_inhibited) {
295 /* Inhibition state has changed. */
296 lock();
297 rs->ostate |= OSWREADY;
298 if (tp->tty_inhibited) rs->ostate &= ~OSWREADY;
299 unlock();
300 rs->inhibited = tp->tty_inhibited;
301 }
302
303 if (rs->drain) {
304 /* Wait for the line to drain then reconfigure and continue output. */
305 if (rs->ocount > 0) return 0;
306 rs->drain = FALSE;
307 rs_config(rs);
308 }
309
310 /* While there is something to do. */
311 for (;;) {
312 ocount = buflen(rs->obuf) - rs->ocount;
313 count = bufend(rs->obuf) - rs->ohead;
314 if (count > ocount) count = ocount;
315 if (count > tp->tty_outleft) count = tp->tty_outleft;
316 if (count == 0 || tp->tty_inhibited) {
317 if (try) return 0;
318 break;
319 }
320 if (try) return 1;
321
322 /* Copy from user space to the RS232 output buffer. */
323 sys_vircopy(tp->tty_outproc, D, (vir_bytes) tp->tty_out_vir,
324 SELF, D, (vir_bytes) rs->ohead, (phys_bytes) count);
325
326 /* Perform output processing on the output buffer. */
327 out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount);
328 if (count == 0) break;
329
330 /* Assume echoing messed up by output. */
331 tp->tty_reprint = TRUE;
332
333 /* Bookkeeping. */
334 lock(); /* protect interrupt sensitive rs->ocount */
335 rs->ocount += ocount;
336 rs_ostart(rs);
337 unlock();
338 if ((rs->ohead += ocount) >= bufend(rs->obuf))
339 rs->ohead -= buflen(rs->obuf);
340 tp->tty_out_vir += count;
341 tp->tty_outcum += count;
342 if ((tp->tty_outleft -= count) == 0) {
343 /* Output is finished, reply to the writer. */
344 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
345 tp->tty_outproc, tp->tty_outcum);
346 tp->tty_outcum = 0;
347 }
348 }
349 if (tp->tty_outleft > 0 && tp->tty_termios.c_ospeed == B0) {
350 /* Oops, the line has hung up. */
351 tty_reply(tp->tty_outrepcode, tp->tty_outcaller, tp->tty_outproc, EIO);
352 tp->tty_outleft = tp->tty_outcum = 0;
353 }
354
355 return 1;
356 }
357
358 /*===========================================================================*
359 * rs_echo *
360 *===========================================================================*/
361 PRIVATE void rs_echo(tp, c)
362 tty_t *tp; /* which TTY */
363 int c; /* character to echo */
364 {
365 /* Echo one character. (Like rs_write, but only one character, optionally.) */
366
367 rs232_t *rs = tp->tty_priv;
368 int count, ocount;
369
370 ocount = buflen(rs->obuf) - rs->ocount;
371 if (ocount == 0) return; /* output buffer full */
372 count = 1;
373 *rs->ohead = c; /* add one character */
374
375 out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount);
376 if (count == 0) return;
377
378 lock();
379 rs->ocount += ocount;
380 rs_ostart(rs);
381 unlock();
382 if ((rs->ohead += ocount) >= bufend(rs->obuf)) rs->ohead -= buflen(rs->obuf);
383 }
384
385 /*===========================================================================*
386 * rs_ioctl *
387 *===========================================================================*/
388 PRIVATE int rs_ioctl(tp, dummy)
389 tty_t *tp; /* which TTY */
390 int dummy;
391 {
392 /* Reconfigure the line as soon as the output has drained. */
393 rs232_t *rs = tp->tty_priv;
394
395 rs->drain = TRUE;
396 return 0; /* dummy */
397 }
398
399 /*===========================================================================*
400 * rs_config *
401 *===========================================================================*/
402 PRIVATE void rs_config(rs)
403 rs232_t *rs; /* which line */
404 {
405 /* Set various line control parameters for RS232 I/O.
406 * If DataBits == 5 and StopBits == 2, 8250 will generate 1.5 stop bits.
407 * The 8250 can't handle split speed, so we use the input speed.
408 */
409
410 tty_t *tp = rs->tty;
411 int divisor;
412 int line_controls;
413 static struct speed2divisor {
414 speed_t speed;
415 int divisor;
416 } s2d[] = {
417 #if (MACHINE == IBM_PC)
418 { B50, UART_FREQ / 50 },
419 #endif
420 { B75, UART_FREQ / 75 },
421 { B110, UART_FREQ / 110 },
422 { B134, UART_FREQ * 10 / 1345 },
423 { B150, UART_FREQ / 150 },
424 { B200, UART_FREQ / 200 },
425 { B300, UART_FREQ / 300 },
426 { B600, UART_FREQ / 600 },
427 { B1200, UART_FREQ / 1200 },
428 #if (MACHINE == IBM_PC)
429 { B1800, UART_FREQ / 1800 },
430 #endif
431 { B2400, UART_FREQ / 2400 },
432 { B4800, UART_FREQ / 4800 },
433 { B9600, UART_FREQ / 9600 },
434 { B19200, UART_FREQ / 19200 },
435 #if (MACHINE == IBM_PC)
436 { B38400, UART_FREQ / 38400 },
437 { B57600, UART_FREQ / 57600 },
438 { B115200, UART_FREQ / 115200L },
439 #endif
440 };
441 struct speed2divisor *s2dp;
442
443 /* RS232 needs to know the xoff character, and if CTS works. */
444 rs->oxoff = tp->tty_termios.c_cc[VSTOP];
445 rs->cts = (tp->tty_termios.c_cflag & CLOCAL) ? MS_CTS : 0;
446
447 /* Look up the 8250 rate divisor from the output speed. */
448 divisor = 0;
449 for (s2dp = s2d; s2dp < s2d + sizeof(s2d)/sizeof(s2d[0]); s2dp++) {
450 if (s2dp->speed == tp->tty_termios.c_ospeed) divisor = s2dp->divisor;
451 }
452 if (divisor == 0) return; /* B0? */
453
454 #if (MACHINE == IBM_PC)
455 /* Compute line control flag bits. */
456 line_controls = 0;
457 if (tp->tty_termios.c_cflag & PARENB) {
458 line_controls |= LC_PARITY;
459 if (!(tp->tty_termios.c_cflag & PARODD)) line_controls |= LC_PAREVEN;
460 }
461 if (divisor >= (UART_FREQ / 110)) line_controls |= LC_2STOP_BITS;
462 line_controls |= (tp->tty_termios.c_cflag & CSIZE) >> 2;
463
464 /* Lock out interrupts while setting the speed. The receiver register is
465 * going to be hidden by the div_low register, but the input interrupt
466 * handler relies on reading it to clear the interrupt and avoid looping
467 * forever.
468 */
469 lock();
470
471 /* Select the baud rate divisor registers and change the rate. */
472 sys_outb(rs->line_ctl_port, LC_ADDRESS_DIVISOR);
473 sys_outb(rs->div_low_port, divisor);
474 sys_outb(rs->div_hi_port, divisor >> 8);
475
476 /* Change the line controls and reselect the usual registers. */
477 sys_outb(rs->line_ctl_port, line_controls);
478
479 rs->ostate = devready(rs) | ORAW | OSWREADY; /* reads modem_ctl_port */
480 if ((tp->tty_termios.c_lflag & IXON) && rs->oxoff != _POSIX_VDISABLE)
481 rs->ostate &= ~ORAW;
482
483 unlock();
484
485 #else /* MACHINE == ATARI */
486
487 line_controls = U_Q16;
488 if (tp->tty_termios.c_cflag & PARENB) {
489 line_controls |= U_PAR;
490 if (!(tp->tty_termios.c_cflag & PARODD)) line_controls |= U_EVEN;
491 }
492 line_controls |= (divisor >= (UART_FREQ / 110)) ? U_ST2 : U_ST1;
493
494 switch (tp->tty_termios.c_cflag & CSIZE) { /* XXX - are U_Dn like CSn? */
495 case CS5: line_controls |= U_D5; break;
496 case CS5: line_controls |= U_D6; break;
497 case CS5: line_controls |= U_D7; break;
498 case CS5: line_controls |= U_D8; break;
499 }
500 lock();
501 MFP->mf_ucr = line_controls;
502 MFP->mf_tddr = divisor;
503 unlock();
504 #endif /* MACHINE == ATARI */
505 }
506
507 /*===========================================================================*
508 * rs_init *
509 *===========================================================================*/
510 PUBLIC void rs_init(tp)
511 tty_t *tp; /* which TTY */
512 {
513 int dummy;
514 /* Initialize RS232 for one line. */
515
516 register rs232_t *rs;
517 int line;
518 #if (MACHINE == IBM_PC)
519 port_t this_8250;
520 int irq;
521 long v;
522 #endif
523
524 /* Associate RS232 and TTY structures. */
525 line = tp - &tty_table[NR_CONS];
526 rs = tp->tty_priv = &rs_lines[line];
527 rs->tty = tp;
528
529 /* Set up input queue. */
530 rs->ihead = rs->itail = rs->ibuf;
531
532 #if (MACHINE == IBM_PC)
533 /* Precalculate port numbers for speed. Magic numbers in the code (once). */
534 this_8250 = addr_8250[line];
535 rs->xmit_port = this_8250 + 0;
536 rs->recv_port = this_8250 + 0;
537 rs->div_low_port = this_8250 + 0;
538 rs->div_hi_port = this_8250 + 1;
539 rs->int_enab_port = this_8250 + 1;
540 rs->int_id_port = this_8250 + 2;
541 rs->line_ctl_port = this_8250 + 3;
542 rs->modem_ctl_port = this_8250 + 4;
543 rs->line_status_port = this_8250 + 5;
544 rs->modem_status_port = this_8250 + 6;
545 #endif
546
547 /* Set up the hardware to a base state, in particular
548 * o turn off DTR (MC_DTR) to try to stop the external device.
549 * o be careful about the divisor latch. Some BIOS's leave it enabled
550 * here and that caused trouble (no interrupts) in version 1.5 by
551 * hiding the interrupt enable port in the next step, and worse trouble
552 * (continual interrupts) in an old version by hiding the receiver
553 * port in the first interrupt. Call rs_ioctl() early to avoid this.
554 * o disable interrupts at the chip level, to force an edge transition
555 * on the 8259 line when interrupts are next enabled and active.
556 * RS232 interrupts are guaranteed to be disabled now by the 8259
557 * mask, but there used to be trouble if the mask was set without
558 * handling a previous interrupt.
559 */
560 istop(rs); /* sets modem_ctl_port */
561 rs_config(rs);
562 #if (MACHINE == IBM_PC)
563 sys_outb(rs->int_enab_port, 0);
564 #endif
565
566 /* Clear any harmful leftover interrupts. An output interrupt is harmless
567 * and will occur when interrupts are enabled anyway. Set up the output
568 * queue using the status from clearing the modem status interrupt.
569 */
570 #if (MACHINE == IBM_PC)
571 sys_inb(rs->line_status_port, &dummy);
572 sys_inb(rs->recv_port, &dummy);
573 #endif
574 rs->ostate = devready(rs) | ORAW | OSWREADY; /* reads modem_ctl_port */
575 rs->ohead = rs->otail = rs->obuf;
576
577 #if (MACHINE == IBM_PC)
578 /* Enable interrupts for both interrupt controller and device. */
579 irq = (line & 1) == 0 ? RS232_IRQ : SECONDARY_IRQ;
580
581 rs->irq = irq;
582 rs->irq_hook_id = rs->irq; /* call back with irq line number */
583 if (sys_irqsetpolicy(irq, IRQ_REENABLE, &rs->irq_hook_id) != OK) {
584 printf("RS232: Couldn't obtain hook for irq %d\n", irq);
585 } else {
586 if (sys_irqenable(&rs->irq_hook_id) != OK) {
587 printf("RS232: Couldn't enable irq %d (hooked)\n", irq);
588 }
589 }
590
591 rs_irq_set |= (1 << irq);
592
593 sys_outb(rs->int_enab_port, IE_LINE_STATUS_CHANGE | IE_MODEM_STATUS_CHANGE
594 | IE_RECEIVER_READY | IE_TRANSMITTER_READY);
595 #else /* MACHINE == ATARI */
596 /* Initialize the 68901 chip, then enable interrupts. */
597 MFP->mf_scr = 0x00;
598 MFP->mf_tcdcr |= T_Q004;
599 MFP->mf_rsr = R_ENA;
600 MFP->mf_tsr = T_ENA;
601 MFP->mf_aer = (MFP->mf_aer | (IO_SCTS|IO_SDCD)) ^
602 (MFP->mf_gpip & (IO_SCTS|IO_SDCD));
603 MFP->mf_ddr = (MFP->mf_ddr & ~ (IO_SCTS|IO_SDCD));
604 MFP->mf_iera |= (IA_RRDY|IA_RERR|IA_TRDY|IA_TERR);
605 MFP->mf_imra |= (IA_RRDY|IA_RERR|IA_TRDY|IA_TERR);
606 MFP->mf_ierb |= (IB_SCTS|IB_SDCD);
607 MFP->mf_imrb |= (IB_SCTS|IB_SDCD);
608 #endif /* MACHINE == ATARI */
609
610 /* Fill in TTY function hooks. */
611 tp->tty_devread = rs_read;
612 tp->tty_devwrite = rs_write;
613 tp->tty_echo = rs_echo;
614 tp->tty_icancel = rs_icancel;
615 tp->tty_ocancel = rs_ocancel;
616 tp->tty_ioctl = rs_ioctl;
617 tp->tty_break = rs_break;
618 tp->tty_close = rs_close;
619
620 /* Tell external device we are ready. */
621 istart(rs);
622
623 }
624
625 /*===========================================================================*
626 * rs_interrupt *
627 *===========================================================================*/
628 PUBLIC void rs_interrupt(m)
629 message *m; /* which TTY */
630 {
631 unsigned long irq_set;
632 int i;
633 rs232_t *rs;
634
635 irq_set= m->NOTIFY_ARG;
636 for (i= 0, rs = rs_lines; i<NR_RS_LINES; i++, rs++)
637 {
638 if (irq_set & (1 << rs->irq))
639 rs232_handler(rs);
640 }
641 }
642
643 /*===========================================================================*
644 * rs_icancel *
645 *===========================================================================*/
646 PRIVATE int rs_icancel(tp, dummy)
647 tty_t *tp; /* which TTY */
648 int dummy;
649 {
650 /* Cancel waiting input. */
651 rs232_t *rs = tp->tty_priv;
652
653 lock();
654 rs->icount = 0;
655 rs->itail = rs->ihead;
656 istart(rs);
657 unlock();
658
659 return 0; /* dummy */
660 }
661
662 /*===========================================================================*
663 * rs_ocancel *
664 *===========================================================================*/
665 PRIVATE int rs_ocancel(tp, dummy)
666 tty_t *tp; /* which TTY */
667 int dummy;
668 {
669 /* Cancel pending output. */
670 rs232_t *rs = tp->tty_priv;
671
672 lock();
673 rs->ostate &= ~(ODONE | OQUEUED);
674 rs->ocount = 0;
675 rs->otail = rs->ohead;
676 unlock();
677
678 return 0; /* dummy */
679 }
680
681 /*===========================================================================*
682 * rs_read *
683 *===========================================================================*/
684 PRIVATE int rs_read(tp, try)
685 tty_t *tp; /* which tty */
686 int try;
687 {
688 /* Process characters from the circular input buffer. */
689
690 rs232_t *rs = tp->tty_priv;
691 int icount, count, ostate;
692
693 if (!(tp->tty_termios.c_cflag & CLOCAL)) {
694 if (try) return 1;
695 /* Send a SIGHUP if hangup detected. */
696 lock();
697 ostate = rs->ostate;
698 rs->ostate &= ~ODEVHUP; /* save ostate, clear DEVHUP */
699 unlock();
700 if (ostate & ODEVHUP) {
701 sigchar(tp, SIGHUP);
702 tp->tty_termios.c_ospeed = B0; /* Disable further I/O. */
703 return;
704 }
705 }
706
707 if (try) {
708 if (rs->icount > 0)
709 return 1;
710 return 0;
711 }
712
713 while ((count = rs->icount) > 0) {
714 icount = bufend(rs->ibuf) - rs->itail;
715 if (count > icount) count = icount;
716
717 /* Perform input processing on (part of) the input buffer. */
718 if ((count = in_process(tp, rs->itail, count)) == 0) break;
719
720 lock(); /* protect interrupt sensitive variables */
721 rs->icount -= count;
722 if (!rs->idevready && rs->icount < RS_ILOWWATER) istart(rs);
723 unlock();
724 if ((rs->itail += count) == bufend(rs->ibuf)) rs->itail = rs->ibuf;
725 }
726 }
727
728 /*===========================================================================*
729 * rs_ostart *
730 *===========================================================================*/
731 PRIVATE void rs_ostart(rs)
732 rs232_t *rs; /* which rs line */
733 {
734 /* Tell RS232 there is something waiting in the output buffer. */
735
736 rs->ostate |= OQUEUED;
737 if (txready(rs)) out_int(rs);
738 }
739
740 /*===========================================================================*
741 * rs_break *
742 *===========================================================================*/
743 PRIVATE int rs_break(tp, dummy)
744 tty_t *tp; /* which tty */
745 int dummy;
746 {
747 /* Generate a break condition by setting the BREAK bit for 0.4 sec. */
748 rs232_t *rs = tp->tty_priv;
749 int line_controls;
750
751 sys_inb(rs->line_ctl_port, &line_controls);
752 sys_outb(rs->line_ctl_port, line_controls | LC_BREAK);
753 /* XXX */
754 /* milli_delay(400); */ /* ouch */
755 printf("RS232 break\n");
756 sys_outb(rs->line_ctl_port, line_controls);
757 return 0; /* dummy */
758 }
759
760 /*===========================================================================*
761 * rs_close *
762 *===========================================================================*/
763 PRIVATE int rs_close(tp, dummy)
764 tty_t *tp; /* which tty */
765 int dummy;
766 {
767 /* The line is closed; optionally hang up. */
768 rs232_t *rs = tp->tty_priv;
769 int r;
770
771 if (tp->tty_termios.c_cflag & HUPCL) {
772 sys_outb(rs->modem_ctl_port, MC_OUT2 | MC_RTS);
773 }
774 return 0; /* dummy */
775 }
776
777 /* Low level (interrupt) routines. */
778
779 #if (MACHINE == IBM_PC)
780 /*===========================================================================*
781 * rs232_handler *
782 *===========================================================================*/
783 PRIVATE void rs232_handler(rs)
784 struct rs232 *rs;
785 {
786 /* Interrupt hander for RS232. */
787
788 while (TRUE) {
789 int v;
790 /* Loop to pick up ALL pending interrupts for device.
791 * This usually just wastes time unless the hardware has a buffer
792 * (and then we have to worry about being stuck in the loop too long).
793 * Unfortunately, some serial cards lock up without this.
794 */
795 sys_inb(rs->int_id_port, &v);
796 switch (v) {
797 case IS_RECEIVER_READY:
798 in_int(rs);
799 continue;
800 case IS_TRANSMITTER_READY:
801 out_int(rs);
802 continue;
803 case IS_MODEM_STATUS_CHANGE:
804 modem_int(rs);
805 continue;
806 case IS_LINE_STATUS_CHANGE:
807 line_int(rs);
808 continue;
809 }
810 return;
811 }
812 }
813 #endif /* MACHINE == IBM_PC */
814
815 #if (MACHINE == ATARI)
816 /*===========================================================================*
817 * siaint *
818 *===========================================================================*/
819 PRIVATE void siaint(type)
820 int type; /* interrupt type */
821 {
822 /* siaint is the rs232 interrupt procedure for Atari ST's. For ST there are
823 * as much as 5 interrupt lines used for rs232. The trap type byte left on the
824 * stack by the assembler interrupt handler identifies the interrupt cause.
825 */
826
827 register unsigned char code;
828 register rs232_t *rs = &rs_lines[0];
829 int s = lock();
830
831 switch (type & 0x00FF)
832 {
833 case 0x00: /* receive buffer full */
834 in_int(rs);
835 break;
836 case 0x01: /* receive error */
837 line_int(rs);
838 break;
839 case 0x02: /* transmit buffer empty */
840 out_int(rs);
841 break;
842 case 0x03: /* transmit error */
843 code = MFP->mf_tsr;
844 if (code & ~(T_ENA | T_UE | T_EMPTY))
845 {
846 printf("sia: transmit error: status=%x\r\n", code);
847 /* MFP->mf_udr = lastchar; */ /* retry */
848 }
849 break;
850 case 0x04: /* modem lines change */
851 modem_int(rs);
852 break;
853 }
854 restore(s);
855 }
856 #endif /* MACHINE == ATARI */
857
858 /*===========================================================================*
859 * in_int *
860 *===========================================================================*/
861 PRIVATE void in_int(rs)
862 register rs232_t *rs; /* line with input interrupt */
863 {
864 /* Read the data which just arrived.
865 * If it is the oxoff char, clear OSWREADY, else if OSWREADY was clear, set
866 * it and restart output (any char does this, not just xon).
867 * Put data in the buffer if room, otherwise discard it.
868 * Set a flag for the clock interrupt handler to eventually notify TTY.
869 */
870
871 int c;
872
873 #if (MACHINE == IBM_PC)
874 sys_inb(rs->recv_port, &c);
875 #else /* MACHINE == ATARI */
876 c = MFP->mf_udr;
877 #endif
878
879 if (!(rs->ostate & ORAW)) {
880 if (c == rs->oxoff) {
881 rs->ostate &= ~OSWREADY;
882 } else
883 if (!(rs->ostate & OSWREADY)) {
884 rs->ostate |= OSWREADY;
885 if (txready(rs)) out_int(rs);
886 }
887 }
888
889 if (rs->icount == buflen(rs->ibuf)) return; /* input buffer full, discard */
890
891 if (++rs->icount == RS_IHIGHWATER && rs->idevready) istop(rs);
892 *rs->ihead = c;
893 if (++rs->ihead == bufend(rs->ibuf)) rs->ihead = rs->ibuf;
894 if (rs->icount == 1) {
895 rs->tty->tty_events = 1;
896 force_timeout();
897 }
898 }
899
900 /*===========================================================================*
901 * line_int *
902 *===========================================================================*/
903 PRIVATE void line_int(rs)
904 register rs232_t *rs; /* line with line status interrupt */
905 {
906 /* Check for and record errors. */
907
908 #if (MACHINE == IBM_PC)
909 sys_inb(rs->line_status_port, &rs->lstatus);
910 #else /* MACHINE == ATARI */
911 rs->lstatus = MFP->mf_rsr;
912 MFP->mf_rsr &= R_ENA;
913 rs->pad = MFP->mf_udr; /* discard char in case of LS_OVERRUN_ERR */
914 #endif /* MACHINE == ATARI */
915 if (rs->lstatus & LS_FRAMING_ERR) ++rs->framing_errors;
916 if (rs->lstatus & LS_OVERRUN_ERR) ++rs->overrun_errors;
917 if (rs->lstatus & LS_PARITY_ERR) ++rs->parity_errors;
918 if (rs->lstatus & LS_BREAK_INTERRUPT) ++rs->break_interrupts;
919 }
920
921 /*===========================================================================*
922 * modem_int *
923 *===========================================================================*/
924 PRIVATE void modem_int(rs)
925 register rs232_t *rs; /* line with modem interrupt */
926 {
927 /* Get possibly new device-ready status, and clear ODEVREADY if necessary.
928 * If the device just became ready, restart output.
929 */
930
931 #if (MACHINE == ATARI)
932 /* Set active edge interrupt so that next change causes a new interrupt */
933 MFP->mf_aer = (MFP->mf_aer | (IO_SCTS|IO_SDCD)) ^
934 (MFP->mf_gpip & (IO_SCTS|IO_SDCD));
935 #endif
936
937 if (devhup(rs)) {
938 rs->ostate |= ODEVHUP;
939 rs->tty->tty_events = 1;
940 force_timeout();
941 }
942
943 if (!devready(rs))
944 rs->ostate &= ~ODEVREADY;
945 else if (!(rs->ostate & ODEVREADY)) {
946 rs->ostate |= ODEVREADY;
947 if (txready(rs)) out_int(rs);
948 }
949 }
950
951 /*===========================================================================*
952 * out_int *
953 *===========================================================================*/
954 PRIVATE void out_int(rs)
955 register rs232_t *rs; /* line with output interrupt */
956 {
957 /* If there is output to do and everything is ready, do it (local device is
958 * known ready).
959 * Notify TTY when the buffer goes empty.
960 */
961
962 if (rs->ostate >= (ODEVREADY | OQUEUED | OSWREADY)) {
963 /* Bit test allows ORAW and requires the others. */
964 #if (MACHINE == IBM_PC)
965 sys_outb(rs->xmit_port, *rs->otail);
966 #else /* MACHINE == ATARI */
967 MFP->mf_udr = *rs->otail;
968 #endif
969 if (++rs->otail == bufend(rs->obuf)) rs->otail = rs->obuf;
970 if (--rs->ocount == 0) {
971 rs->ostate ^= (ODONE | OQUEUED); /* ODONE on, OQUEUED off */
972 rs->tty->tty_events = 1;
973 force_timeout();
974 } else
975 if (rs->ocount == RS_OLOWWATER) { /* running low? */
976 rs->tty->tty_events = 1;
977 force_timeout();
978 }
979 }
980 }
981 #endif /* NR_RS_LINES > 0 */
982
Cache object: f55072f196e4104f8958a23745edac5b
|