source: trunk/minix/drivers/tty/rs232.c@ 20

Last change on this file since 20 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 29.8 KB
RevLine 
[9]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. */
175typedef unsigned char bool_t; /* boolean */
176
177/* RS232 device structure, one per device. */
178typedef 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
235PUBLIC rs232_t rs_lines[NR_RS_LINES];
236
237/* Table and macro to translate an RS232 line number to its rs_lines entry. */
238PRIVATE 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. */
244PRIVATE port_t addr_8250[] = {
245 0x3F8, /* COM1 */
246 0x2F8, /* COM2 */
247 0x3E8, /* COM3 */
248 0x2E8, /* COM4 */
249};
250#endif
251
252FORWARD _PROTOTYPE( void in_int, (rs232_t *rs) );
253FORWARD _PROTOTYPE( void line_int, (rs232_t *rs) );
254FORWARD _PROTOTYPE( void modem_int, (rs232_t *rs) );
255FORWARD _PROTOTYPE( int rs_write, (tty_t *tp, int try) );
256FORWARD _PROTOTYPE( void rs_echo, (tty_t *tp, int c) );
257FORWARD _PROTOTYPE( int rs_ioctl, (tty_t *tp, int try) );
258FORWARD _PROTOTYPE( void rs_config, (rs232_t *rs) );
259FORWARD _PROTOTYPE( int rs_read, (tty_t *tp, int try) );
260FORWARD _PROTOTYPE( int rs_icancel, (tty_t *tp, int try) );
261FORWARD _PROTOTYPE( int rs_ocancel, (tty_t *tp, int try) );
262FORWARD _PROTOTYPE( void rs_ostart, (rs232_t *rs) );
263FORWARD _PROTOTYPE( int rs_break, (tty_t *tp, int try) );
264FORWARD _PROTOTYPE( int rs_close, (tty_t *tp, int try) );
265FORWARD _PROTOTYPE( void out_int, (rs232_t *rs) );
266FORWARD _PROTOTYPE( void rs232_handler, (rs232_t *rs) );
267
268/* XXX */
269PRIVATE void lock(void) {}
270PRIVATE void unlock(void) {}
271
272PRIVATE int my_inb(port_t port)
273{
274 int r;
275 unsigned long v = 0;
276 r = sys_inb(port, &v);
277 if (r != OK)
278 printf("RS232 warning: failed inb 0x%x\n", port);
279
280 return (int) v;
281}
282
283/*===========================================================================*
284 * rs_write *
285 *===========================================================================*/
286PRIVATE int rs_write(tp, try)
287register tty_t *tp;
288int try;
289{
290/* (*devwrite)() routine for RS232. */
291
292 rs232_t *rs = tp->tty_priv;
293 int count, ocount;
294
295 if (rs->inhibited != tp->tty_inhibited) {
296 /* Inhibition state has changed. */
297 lock();
298 rs->ostate |= OSWREADY;
299 if (tp->tty_inhibited) rs->ostate &= ~OSWREADY;
300 unlock();
301 rs->inhibited = tp->tty_inhibited;
302 }
303
304 if (rs->drain) {
305 /* Wait for the line to drain then reconfigure and continue output. */
306 if (rs->ocount > 0) return 0;
307 rs->drain = FALSE;
308 rs_config(rs);
309 }
310
311 /* While there is something to do. */
312 for (;;) {
313 ocount = buflen(rs->obuf) - rs->ocount;
314 count = bufend(rs->obuf) - rs->ohead;
315 if (count > ocount) count = ocount;
316 if (count > tp->tty_outleft) count = tp->tty_outleft;
317 if (count == 0 || tp->tty_inhibited) {
318 if (try) return 0;
319 break;
320 }
321 if (try) return 1;
322
323 /* Copy from user space to the RS232 output buffer. */
324 sys_vircopy(tp->tty_outproc, D, (vir_bytes) tp->tty_out_vir,
325 SELF, D, (vir_bytes) rs->ohead, (phys_bytes) count);
326
327 /* Perform output processing on the output buffer. */
328 out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount);
329 if (count == 0) break;
330
331 /* Assume echoing messed up by output. */
332 tp->tty_reprint = TRUE;
333
334 /* Bookkeeping. */
335 lock(); /* protect interrupt sensitive rs->ocount */
336 rs->ocount += ocount;
337 rs_ostart(rs);
338 unlock();
339 if ((rs->ohead += ocount) >= bufend(rs->obuf))
340 rs->ohead -= buflen(rs->obuf);
341 tp->tty_out_vir += count;
342 tp->tty_outcum += count;
343 if ((tp->tty_outleft -= count) == 0) {
344 /* Output is finished, reply to the writer. */
345 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
346 tp->tty_outproc, tp->tty_outcum);
347 tp->tty_outcum = 0;
348 }
349 }
350 if (tp->tty_outleft > 0 && tp->tty_termios.c_ospeed == B0) {
351 /* Oops, the line has hung up. */
352 tty_reply(tp->tty_outrepcode, tp->tty_outcaller, tp->tty_outproc, EIO);
353 tp->tty_outleft = tp->tty_outcum = 0;
354 }
355
356 return 1;
357}
358
359/*===========================================================================*
360 * rs_echo *
361 *===========================================================================*/
362PRIVATE void rs_echo(tp, c)
363tty_t *tp; /* which TTY */
364int c; /* character to echo */
365{
366/* Echo one character. (Like rs_write, but only one character, optionally.) */
367
368 rs232_t *rs = tp->tty_priv;
369 int count, ocount;
370
371 ocount = buflen(rs->obuf) - rs->ocount;
372 if (ocount == 0) return; /* output buffer full */
373 count = 1;
374 *rs->ohead = c; /* add one character */
375
376 out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount);
377 if (count == 0) return;
378
379 lock();
380 rs->ocount += ocount;
381 rs_ostart(rs);
382 unlock();
383 if ((rs->ohead += ocount) >= bufend(rs->obuf)) rs->ohead -= buflen(rs->obuf);
384}
385
386/*===========================================================================*
387 * rs_ioctl *
388 *===========================================================================*/
389PRIVATE int rs_ioctl(tp, dummy)
390tty_t *tp; /* which TTY */
391int dummy;
392{
393/* Reconfigure the line as soon as the output has drained. */
394 rs232_t *rs = tp->tty_priv;
395
396 rs->drain = TRUE;
397 return 0; /* dummy */
398}
399
400/*===========================================================================*
401 * rs_config *
402 *===========================================================================*/
403PRIVATE void rs_config(rs)
404rs232_t *rs; /* which line */
405{
406/* Set various line control parameters for RS232 I/O.
407 * If DataBits == 5 and StopBits == 2, 8250 will generate 1.5 stop bits.
408 * The 8250 can't handle split speed, so we use the input speed.
409 */
410
411 tty_t *tp = rs->tty;
412 int divisor;
413 int line_controls;
414 static struct speed2divisor {
415 speed_t speed;
416 int divisor;
417 } s2d[] = {
418#if (MACHINE == IBM_PC)
419 { B50, UART_FREQ / 50 },
420#endif
421 { B75, UART_FREQ / 75 },
422 { B110, UART_FREQ / 110 },
423 { B134, UART_FREQ * 10 / 1345 },
424 { B150, UART_FREQ / 150 },
425 { B200, UART_FREQ / 200 },
426 { B300, UART_FREQ / 300 },
427 { B600, UART_FREQ / 600 },
428 { B1200, UART_FREQ / 1200 },
429#if (MACHINE == IBM_PC)
430 { B1800, UART_FREQ / 1800 },
431#endif
432 { B2400, UART_FREQ / 2400 },
433 { B4800, UART_FREQ / 4800 },
434 { B9600, UART_FREQ / 9600 },
435 { B19200, UART_FREQ / 19200 },
436#if (MACHINE == IBM_PC)
437 { B38400, UART_FREQ / 38400 },
438 { B57600, UART_FREQ / 57600 },
439 { B115200, UART_FREQ / 115200L },
440#endif
441 };
442 struct speed2divisor *s2dp;
443
444 /* RS232 needs to know the xoff character, and if CTS works. */
445 rs->oxoff = tp->tty_termios.c_cc[VSTOP];
446 rs->cts = (tp->tty_termios.c_cflag & CLOCAL) ? MS_CTS : 0;
447
448 /* Look up the 8250 rate divisor from the output speed. */
449 divisor = 0;
450 for (s2dp = s2d; s2dp < s2d + sizeof(s2d)/sizeof(s2d[0]); s2dp++) {
451 if (s2dp->speed == tp->tty_termios.c_ospeed) divisor = s2dp->divisor;
452 }
453 if (divisor == 0) return; /* B0? */
454
455#if (MACHINE == IBM_PC)
456 /* Compute line control flag bits. */
457 line_controls = 0;
458 if (tp->tty_termios.c_cflag & PARENB) {
459 line_controls |= LC_PARITY;
460 if (!(tp->tty_termios.c_cflag & PARODD)) line_controls |= LC_PAREVEN;
461 }
462 if (divisor >= (UART_FREQ / 110)) line_controls |= LC_2STOP_BITS;
463 line_controls |= (tp->tty_termios.c_cflag & CSIZE) >> 2;
464
465 /* Lock out interrupts while setting the speed. The receiver register is
466 * going to be hidden by the div_low register, but the input interrupt
467 * handler relies on reading it to clear the interrupt and avoid looping
468 * forever.
469 */
470 lock();
471
472 /* Select the baud rate divisor registers and change the rate. */
473 sys_outb(rs->line_ctl_port, LC_ADDRESS_DIVISOR);
474 sys_outb(rs->div_low_port, divisor);
475 sys_outb(rs->div_hi_port, divisor >> 8);
476
477 /* Change the line controls and reselect the usual registers. */
478 sys_outb(rs->line_ctl_port, line_controls);
479
480 rs->ostate = devready(rs) | ORAW | OSWREADY; /* reads modem_ctl_port */
481 if ((tp->tty_termios.c_lflag & IXON) && rs->oxoff != _POSIX_VDISABLE)
482 rs->ostate &= ~ORAW;
483
484 unlock();
485
486#else /* MACHINE == ATARI */
487
488 line_controls = U_Q16;
489 if (tp->tty_termios.c_cflag & PARENB) {
490 line_controls |= U_PAR;
491 if (!(tp->tty_termios.c_cflag & PARODD)) line_controls |= U_EVEN;
492 }
493 line_controls |= (divisor >= (UART_FREQ / 110)) ? U_ST2 : U_ST1;
494
495 switch (tp->tty_termios.c_cflag & CSIZE) { /* XXX - are U_Dn like CSn? */
496 case CS5: line_controls |= U_D5; break;
497 case CS5: line_controls |= U_D6; break;
498 case CS5: line_controls |= U_D7; break;
499 case CS5: line_controls |= U_D8; break;
500 }
501 lock();
502 MFP->mf_ucr = line_controls;
503 MFP->mf_tddr = divisor;
504 unlock();
505#endif /* MACHINE == ATARI */
506}
507
508/*===========================================================================*
509 * rs_init *
510 *===========================================================================*/
511PUBLIC void rs_init(tp)
512tty_t *tp; /* which TTY */
513{
514 unsigned long dummy;
515/* Initialize RS232 for one line. */
516
517 register rs232_t *rs;
518 int line;
519#if (MACHINE == IBM_PC)
520 port_t this_8250;
521 int irq;
522 long v;
523#endif
524
525 /* Associate RS232 and TTY structures. */
526 line = tp - &tty_table[NR_CONS];
527 rs = tp->tty_priv = &rs_lines[line];
528 rs->tty = tp;
529
530 /* Set up input queue. */
531 rs->ihead = rs->itail = rs->ibuf;
532
533#if (MACHINE == IBM_PC)
534 /* Precalculate port numbers for speed. Magic numbers in the code (once). */
535 this_8250 = addr_8250[line];
536 rs->xmit_port = this_8250 + 0;
537 rs->recv_port = this_8250 + 0;
538 rs->div_low_port = this_8250 + 0;
539 rs->div_hi_port = this_8250 + 1;
540 rs->int_enab_port = this_8250 + 1;
541 rs->int_id_port = this_8250 + 2;
542 rs->line_ctl_port = this_8250 + 3;
543 rs->modem_ctl_port = this_8250 + 4;
544 rs->line_status_port = this_8250 + 5;
545 rs->modem_status_port = this_8250 + 6;
546#endif
547
548 /* Set up the hardware to a base state, in particular
549 * o turn off DTR (MC_DTR) to try to stop the external device.
550 * o be careful about the divisor latch. Some BIOS's leave it enabled
551 * here and that caused trouble (no interrupts) in version 1.5 by
552 * hiding the interrupt enable port in the next step, and worse trouble
553 * (continual interrupts) in an old version by hiding the receiver
554 * port in the first interrupt. Call rs_ioctl() early to avoid this.
555 * o disable interrupts at the chip level, to force an edge transition
556 * on the 8259 line when interrupts are next enabled and active.
557 * RS232 interrupts are guaranteed to be disabled now by the 8259
558 * mask, but there used to be trouble if the mask was set without
559 * handling a previous interrupt.
560 */
561 istop(rs); /* sets modem_ctl_port */
562 rs_config(rs);
563#if (MACHINE == IBM_PC)
564 sys_outb(rs->int_enab_port, 0);
565#endif
566
567 /* Clear any harmful leftover interrupts. An output interrupt is harmless
568 * and will occur when interrupts are enabled anyway. Set up the output
569 * queue using the status from clearing the modem status interrupt.
570 */
571#if (MACHINE == IBM_PC)
572 sys_inb(rs->line_status_port, &dummy);
573 sys_inb(rs->recv_port, &dummy);
574#endif
575 rs->ostate = devready(rs) | ORAW | OSWREADY; /* reads modem_ctl_port */
576 rs->ohead = rs->otail = rs->obuf;
577
578#if (MACHINE == IBM_PC)
579 /* Enable interrupts for both interrupt controller and device. */
580 irq = (line & 1) == 0 ? RS232_IRQ : SECONDARY_IRQ;
581
582 rs->irq = irq;
583 rs->irq_hook_id = rs->irq; /* call back with irq line number */
584 if (sys_irqsetpolicy(irq, IRQ_REENABLE, &rs->irq_hook_id) != OK) {
585 printf("RS232: Couldn't obtain hook for irq %d\n", irq);
586 } else {
587 if (sys_irqenable(&rs->irq_hook_id) != OK) {
588 printf("RS232: Couldn't enable irq %d (hooked)\n", irq);
589 }
590 }
591
592 rs_irq_set |= (1 << irq);
593
594 sys_outb(rs->int_enab_port, IE_LINE_STATUS_CHANGE | IE_MODEM_STATUS_CHANGE
595 | IE_RECEIVER_READY | IE_TRANSMITTER_READY);
596#else /* MACHINE == ATARI */
597 /* Initialize the 68901 chip, then enable interrupts. */
598 MFP->mf_scr = 0x00;
599 MFP->mf_tcdcr |= T_Q004;
600 MFP->mf_rsr = R_ENA;
601 MFP->mf_tsr = T_ENA;
602 MFP->mf_aer = (MFP->mf_aer | (IO_SCTS|IO_SDCD)) ^
603 (MFP->mf_gpip & (IO_SCTS|IO_SDCD));
604 MFP->mf_ddr = (MFP->mf_ddr & ~ (IO_SCTS|IO_SDCD));
605 MFP->mf_iera |= (IA_RRDY|IA_RERR|IA_TRDY|IA_TERR);
606 MFP->mf_imra |= (IA_RRDY|IA_RERR|IA_TRDY|IA_TERR);
607 MFP->mf_ierb |= (IB_SCTS|IB_SDCD);
608 MFP->mf_imrb |= (IB_SCTS|IB_SDCD);
609#endif /* MACHINE == ATARI */
610
611 /* Fill in TTY function hooks. */
612 tp->tty_devread = rs_read;
613 tp->tty_devwrite = rs_write;
614 tp->tty_echo = rs_echo;
615 tp->tty_icancel = rs_icancel;
616 tp->tty_ocancel = rs_ocancel;
617 tp->tty_ioctl = rs_ioctl;
618 tp->tty_break = rs_break;
619 tp->tty_close = rs_close;
620
621 /* Tell external device we are ready. */
622 istart(rs);
623
624}
625
626/*===========================================================================*
627 * rs_interrupt *
628 *===========================================================================*/
629PUBLIC void rs_interrupt(m)
630message *m; /* which TTY */
631{
632 unsigned long irq_set;
633 int i;
634 rs232_t *rs;
635
636 irq_set= m->NOTIFY_ARG;
637 for (i= 0, rs = rs_lines; i<NR_RS_LINES; i++, rs++)
638 {
639 if (irq_set & (1 << rs->irq))
640 rs232_handler(rs);
641 }
642}
643
644/*===========================================================================*
645 * rs_icancel *
646 *===========================================================================*/
647PRIVATE int rs_icancel(tp, dummy)
648tty_t *tp; /* which TTY */
649int dummy;
650{
651/* Cancel waiting input. */
652 rs232_t *rs = tp->tty_priv;
653
654 lock();
655 rs->icount = 0;
656 rs->itail = rs->ihead;
657 istart(rs);
658 unlock();
659
660 return 0; /* dummy */
661}
662
663/*===========================================================================*
664 * rs_ocancel *
665 *===========================================================================*/
666PRIVATE int rs_ocancel(tp, dummy)
667tty_t *tp; /* which TTY */
668int dummy;
669{
670/* Cancel pending output. */
671 rs232_t *rs = tp->tty_priv;
672
673 lock();
674 rs->ostate &= ~(ODONE | OQUEUED);
675 rs->ocount = 0;
676 rs->otail = rs->ohead;
677 unlock();
678
679 return 0; /* dummy */
680}
681
682/*===========================================================================*
683 * rs_read *
684 *===========================================================================*/
685PRIVATE int rs_read(tp, try)
686tty_t *tp; /* which tty */
687int try;
688{
689/* Process characters from the circular input buffer. */
690
691 rs232_t *rs = tp->tty_priv;
692 int icount, count, ostate;
693
694 if (!(tp->tty_termios.c_cflag & CLOCAL)) {
695 if (try) return 1;
696 /* Send a SIGHUP if hangup detected. */
697 lock();
698 ostate = rs->ostate;
699 rs->ostate &= ~ODEVHUP; /* save ostate, clear DEVHUP */
700 unlock();
701 if (ostate & ODEVHUP) {
702 sigchar(tp, SIGHUP);
703 tp->tty_termios.c_ospeed = B0; /* Disable further I/O. */
704 return;
705 }
706 }
707
708 if (try) {
709 if (rs->icount > 0)
710 return 1;
711 return 0;
712 }
713
714 while ((count = rs->icount) > 0) {
715 icount = bufend(rs->ibuf) - rs->itail;
716 if (count > icount) count = icount;
717
718 /* Perform input processing on (part of) the input buffer. */
719 if ((count = in_process(tp, rs->itail, count)) == 0) break;
720
721 lock(); /* protect interrupt sensitive variables */
722 rs->icount -= count;
723 if (!rs->idevready && rs->icount < RS_ILOWWATER) istart(rs);
724 unlock();
725 if ((rs->itail += count) == bufend(rs->ibuf)) rs->itail = rs->ibuf;
726 }
727}
728
729/*===========================================================================*
730 * rs_ostart *
731 *===========================================================================*/
732PRIVATE void rs_ostart(rs)
733rs232_t *rs; /* which rs line */
734{
735/* Tell RS232 there is something waiting in the output buffer. */
736
737 rs->ostate |= OQUEUED;
738 if (txready(rs)) out_int(rs);
739}
740
741/*===========================================================================*
742 * rs_break *
743 *===========================================================================*/
744PRIVATE int rs_break(tp, dummy)
745tty_t *tp; /* which tty */
746int dummy;
747{
748/* Generate a break condition by setting the BREAK bit for 0.4 sec. */
749 rs232_t *rs = tp->tty_priv;
750 unsigned long line_controls;
751
752 sys_inb(rs->line_ctl_port, &line_controls);
753 sys_outb(rs->line_ctl_port, line_controls | LC_BREAK);
754 /* XXX */
755 /* milli_delay(400); */ /* ouch */
756 printf("RS232 break\n");
757 sys_outb(rs->line_ctl_port, line_controls);
758 return 0; /* dummy */
759}
760
761/*===========================================================================*
762 * rs_close *
763 *===========================================================================*/
764PRIVATE int rs_close(tp, dummy)
765tty_t *tp; /* which tty */
766int dummy;
767{
768/* The line is closed; optionally hang up. */
769 rs232_t *rs = tp->tty_priv;
770 int r;
771
772 if (tp->tty_termios.c_cflag & HUPCL) {
773 sys_outb(rs->modem_ctl_port, MC_OUT2 | MC_RTS);
774 }
775 return 0; /* dummy */
776}
777
778/* Low level (interrupt) routines. */
779
780#if (MACHINE == IBM_PC)
781/*===========================================================================*
782 * rs232_handler *
783 *===========================================================================*/
784PRIVATE void rs232_handler(rs)
785struct rs232 *rs;
786{
787/* Interrupt hander for RS232. */
788
789 while (TRUE) {
790 unsigned long v;
791 /* Loop to pick up ALL pending interrupts for device.
792 * This usually just wastes time unless the hardware has a buffer
793 * (and then we have to worry about being stuck in the loop too long).
794 * Unfortunately, some serial cards lock up without this.
795 */
796 sys_inb(rs->int_id_port, &v);
797 switch (v) {
798 case IS_RECEIVER_READY:
799 in_int(rs);
800 continue;
801 case IS_TRANSMITTER_READY:
802 out_int(rs);
803 continue;
804 case IS_MODEM_STATUS_CHANGE:
805 modem_int(rs);
806 continue;
807 case IS_LINE_STATUS_CHANGE:
808 line_int(rs);
809 continue;
810 }
811 return;
812 }
813}
814#endif /* MACHINE == IBM_PC */
815
816#if (MACHINE == ATARI)
817/*===========================================================================*
818 * siaint *
819 *===========================================================================*/
820PRIVATE void siaint(type)
821int type; /* interrupt type */
822{
823/* siaint is the rs232 interrupt procedure for Atari ST's. For ST there are
824 * as much as 5 interrupt lines used for rs232. The trap type byte left on the
825 * stack by the assembler interrupt handler identifies the interrupt cause.
826 */
827
828 register unsigned char code;
829 register rs232_t *rs = &rs_lines[0];
830 int s = lock();
831
832 switch (type & 0x00FF)
833 {
834 case 0x00: /* receive buffer full */
835 in_int(rs);
836 break;
837 case 0x01: /* receive error */
838 line_int(rs);
839 break;
840 case 0x02: /* transmit buffer empty */
841 out_int(rs);
842 break;
843 case 0x03: /* transmit error */
844 code = MFP->mf_tsr;
845 if (code & ~(T_ENA | T_UE | T_EMPTY))
846 {
847 printf("sia: transmit error: status=%x\r\n", code);
848 /* MFP->mf_udr = lastchar; */ /* retry */
849 }
850 break;
851 case 0x04: /* modem lines change */
852 modem_int(rs);
853 break;
854 }
855 restore(s);
856}
857#endif /* MACHINE == ATARI */
858
859/*===========================================================================*
860 * in_int *
861 *===========================================================================*/
862PRIVATE void in_int(rs)
863register rs232_t *rs; /* line with input interrupt */
864{
865/* Read the data which just arrived.
866 * If it is the oxoff char, clear OSWREADY, else if OSWREADY was clear, set
867 * it and restart output (any char does this, not just xon).
868 * Put data in the buffer if room, otherwise discard it.
869 * Set a flag for the clock interrupt handler to eventually notify TTY.
870 */
871
872 unsigned long c;
873
874#if (MACHINE == IBM_PC)
875 sys_inb(rs->recv_port, &c);
876#else /* MACHINE == ATARI */
877 c = MFP->mf_udr;
878#endif
879
880 if (!(rs->ostate & ORAW)) {
881 if (c == rs->oxoff) {
882 rs->ostate &= ~OSWREADY;
883 } else
884 if (!(rs->ostate & OSWREADY)) {
885 rs->ostate |= OSWREADY;
886 if (txready(rs)) out_int(rs);
887 }
888 }
889
890 if (rs->icount == buflen(rs->ibuf))
891 {
892 printf("in_int: discarding byte\n");
893 return; /* input buffer full, discard */
894 }
895
896 if (++rs->icount == RS_IHIGHWATER && rs->idevready) istop(rs);
897 *rs->ihead = c;
898 if (++rs->ihead == bufend(rs->ibuf)) rs->ihead = rs->ibuf;
899 if (rs->icount == 1) {
900 rs->tty->tty_events = 1;
901 force_timeout();
902 }
903 else
904 printf("in_int: icount = %d\n", rs->icount);
905}
906
907/*===========================================================================*
908 * line_int *
909 *===========================================================================*/
910PRIVATE void line_int(rs)
911register rs232_t *rs; /* line with line status interrupt */
912{
913/* Check for and record errors. */
914
915 unsigned long s;
916#if (MACHINE == IBM_PC)
917 sys_inb(rs->line_status_port, &s);
918 rs->lstatus = s;
919#else /* MACHINE == ATARI */
920 rs->lstatus = MFP->mf_rsr;
921 MFP->mf_rsr &= R_ENA;
922 rs->pad = MFP->mf_udr; /* discard char in case of LS_OVERRUN_ERR */
923#endif /* MACHINE == ATARI */
924 if (rs->lstatus & LS_FRAMING_ERR) ++rs->framing_errors;
925 if (rs->lstatus & LS_OVERRUN_ERR) ++rs->overrun_errors;
926 if (rs->lstatus & LS_PARITY_ERR) ++rs->parity_errors;
927 if (rs->lstatus & LS_BREAK_INTERRUPT) ++rs->break_interrupts;
928}
929
930/*===========================================================================*
931 * modem_int *
932 *===========================================================================*/
933PRIVATE void modem_int(rs)
934register rs232_t *rs; /* line with modem interrupt */
935{
936/* Get possibly new device-ready status, and clear ODEVREADY if necessary.
937 * If the device just became ready, restart output.
938 */
939
940#if (MACHINE == ATARI)
941 /* Set active edge interrupt so that next change causes a new interrupt */
942 MFP->mf_aer = (MFP->mf_aer | (IO_SCTS|IO_SDCD)) ^
943 (MFP->mf_gpip & (IO_SCTS|IO_SDCD));
944#endif
945
946 if (devhup(rs)) {
947 rs->ostate |= ODEVHUP;
948 rs->tty->tty_events = 1;
949 force_timeout();
950 }
951
952 if (!devready(rs))
953 rs->ostate &= ~ODEVREADY;
954 else if (!(rs->ostate & ODEVREADY)) {
955 rs->ostate |= ODEVREADY;
956 if (txready(rs)) out_int(rs);
957 }
958}
959
960/*===========================================================================*
961 * out_int *
962 *===========================================================================*/
963PRIVATE void out_int(rs)
964register rs232_t *rs; /* line with output interrupt */
965{
966/* If there is output to do and everything is ready, do it (local device is
967 * known ready).
968 * Notify TTY when the buffer goes empty.
969 */
970
971 if (rs->ostate >= (ODEVREADY | OQUEUED | OSWREADY)) {
972 /* Bit test allows ORAW and requires the others. */
973#if (MACHINE == IBM_PC)
974 sys_outb(rs->xmit_port, *rs->otail);
975#else /* MACHINE == ATARI */
976 MFP->mf_udr = *rs->otail;
977#endif
978 if (++rs->otail == bufend(rs->obuf)) rs->otail = rs->obuf;
979 if (--rs->ocount == 0) {
980 rs->ostate ^= (ODONE | OQUEUED); /* ODONE on, OQUEUED off */
981 rs->tty->tty_events = 1;
982 force_timeout();
983 } else
984 if (rs->ocount == RS_OLOWWATER) { /* running low? */
985 rs->tty->tty_events = 1;
986 force_timeout();
987 }
988 }
989}
990#endif /* NR_RS_LINES > 0 */
991
Note: See TracBrowser for help on using the repository browser.