/* * Mach Operating System * Copyright (c) 1993-1989 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ /* * File: scc_8530_hdw.c * Author: Alessandro Forin, Carnegie Mellon University * Date: 6/91 * * Hardware-level operations for the SCC Serial Line Driver */ #include #if NSCC > 0 #include #include #include #include /* spl definitions */ #include #include #include #include #include #include /* Alignment and padding */ #if defined(DECSTATION) /* * 3min's padding */ typedef struct { char pad0; volatile unsigned char datum; char pad1[2]; } scc_padded1_register_t; #define scc_register_t scc_padded1_register_t #endif #if defined(FLAMINGO) typedef struct { volatile unsigned int datum; unsigned int pad1; } scc_padded1_register_t; #define scc_register_t scc_padded1_register_t #define scc_set_datum(d,v) (d) = (volatile unsigned int) (v) << 8, wbflush() #define scc_get_datum(d,v) (v) = ((d) >> 8) & 0xff #endif #include /* needs the above defs */ #define private static #define public /* * Forward decls */ private check_car( struct tty *, boolean_t ); /* * On the 3min keyboard and mouse come in on channels A * of the two units. The MI code expects them at 'lines' * 0 and 1, respectively. So we map here back and forth. * Note also the MI code believes unit 0 has four lines. */ #define SCC_KBDUNIT 1 #define SCC_PTRUNIT 0 mi_to_scc(unitp, linep) int *unitp, *linep; { /* only play games on MI 'unit' 0 */ if (*unitp) { /* e.g. by mapping the first four lines specially */ *unitp++; return; } /* always get unit=0 (console) and line = 0|1 */ if (*linep == SCREEN_LINE_KEYBOARD) { *unitp = SCC_KBDUNIT; *linep = SCC_CHANNEL_A; } else if (*linep == SCREEN_LINE_POINTER) { *unitp = SCC_PTRUNIT; *linep = SCC_CHANNEL_A; } else { *unitp = (*linep & 1); *linep = SCC_CHANNEL_B; } /* line 0 is channel B, line 1 is channel A */ } #define NSCC_LINE 2 /* 2 ttys per chip */ /* only care for mapping to ttyno */ scc_to_mi(sccunit, sccline) { if (sccunit > 1) return (sccunit * NSCC_LINE + sccline); /* only for console (first pair of SCCs): */ if (sccline == SCC_CHANNEL_A) return ((!sccunit) & 1); return 2+sccunit; } /* * Driver status */ struct scc_softc { scc_regmap_t *regs; /* software copy of some write regs, for reg |= */ struct softreg { unsigned char wr1; unsigned char wr4; unsigned char wr5; unsigned char wr14; } softr[2]; /* per channel */ unsigned char last_rr0[2]; /* for modem signals */ unsigned short fake; /* missing rs232 bits, channel A */ char polling_mode; char softCAR, osoftCAR; char probed_once; boolean_t full_modem; boolean_t isa_console; } scc_softc_data[NSCC]; typedef struct scc_softc *scc_softc_t; scc_softc_t scc_softc[NSCC]; scc_softCAR(unit, line, on) { mi_to_scc(&unit, &line); if (on) scc_softc[unit]->softCAR |= 1<softCAR &= ~(1 << line); } /* * BRG formula is: * ClockFrequency * BRGconstant = --------------------------- - 2 * 2 * BaudRate * ClockDivider */ /* Speed selections with Pclk=7.3728Mhz, clock x16 */ static short scc_speeds[] = /* 0 50 75 110 134.5 150 200 300 600 1200 1800 2400 */ { 0, 4606, 3070, 2093, 1711, 1534, 1150, 766, 382, 190, 126, 94, /* 4800 9600 19.2k 38.4k */ 46, 22, 10, 4}; /* * Definition of the driver for the auto-configuration program. */ int scc_probe(), scc_intr(); static void scc_attach(); vm_offset_t scc_std[NSCC] = { 0 }; struct bus_device *scc_info[NSCC]; struct bus_driver scc_driver = { scc_probe, 0, scc_attach, 0, scc_std, "scc", scc_info,}; /* * Adapt/Probe/Attach functions */ boolean_t scc_uses_modem_control = FALSE;/* patch this with adb */ set_scc_address( int sccunit, vm_offset_t regs, boolean_t has_modem, boolean_t isa_console) { extern int scc_probe(), scc_param(), scc_start(), scc_putc(), scc_getc(), scc_pollc(), scc_mctl(), scc_softCAR(); scc_std[sccunit] = regs; scc_softc_data[sccunit].full_modem = has_modem & scc_uses_modem_control; scc_softc_data[sccunit].isa_console = isa_console; /* Do this here */ console_probe = scc_probe; console_param = scc_param; console_start = scc_start; console_putc = scc_putc; console_getc = scc_getc; console_pollc = scc_pollc; console_mctl = scc_mctl; console_softCAR = scc_softCAR; } scc_probe( int xxx, struct bus_device *ui) { int sccunit = ui->unit; scc_softc_t scc; register int val; register scc_regmap_t *regs; regs = (scc_regmap_t *)scc_std[sccunit]; if (regs == 0) return 0; /* * See if we are here */ if (check_memory(regs, 0)) { /* no rides today */ return 0; } scc = &scc_softc_data[sccunit]; if (scc->probed_once++){ return 1; } /* * Chip once-only initialization * * NOTE: The wiring we assume is the one on the 3min: * * out A-TxD --> TxD keybd or mouse * in A-RxD --> RxD keybd or mouse * out A-DTR~ --> DTR comm * out A-RTS~ --> RTS comm * in A-CTS~ --> SI comm * in A-DCD~ --> RI comm * in A-SYNCH~--> DSR comm * out B-TxD --> TxD comm * in B-RxD --> RxD comm * in B-RxC --> TRxCB comm * in B-TxC --> RTxCB comm * out B-RTS~ --> SS comm * in B-CTS~ --> CTS comm * in B-DCD~ --> CD comm */ scc_softc[sccunit] = scc; scc->regs = regs; scc->fake = 1<t_addr = (char*)(0x80000000L + (sccunit<<1) + (i&1)); /* do min buffering */ tp->t_state |= TS_MIN; } } /* make sure reg pointer is in known state */ scc_init_reg(regs, SCC_CHANNEL_A); scc_init_reg(regs, SCC_CHANNEL_B); /* reset chip, fully */ scc_write_reg(regs, SCC_CHANNEL_A, SCC_WR9, SCC_WR9_HW_RESET); delay(50000);/*enough ? */ scc_write_reg(regs, SCC_CHANNEL_A, SCC_WR9, 0); /* program the interrupt vector */ scc_write_reg(regs, SCC_CHANNEL_A, SCC_WR2, 0xf0); scc_write_reg(regs, SCC_CHANNEL_B, SCC_WR2, 0xf0); scc_write_reg(regs, SCC_CHANNEL_A, SCC_WR9, SCC_WR9_VIS); /* most of the init is in scc_param() */ /* timing base defaults */ scc->softr[SCC_CHANNEL_A].wr4 = SCC_WR4_CLK_x16; scc->softr[SCC_CHANNEL_B].wr4 = SCC_WR4_CLK_x16; /* enable DTR, RTS and dont SS */ #if 0 /* According to one book I have this signal (pin 23, "SS") is "specified by the provider", meaning the EIA-232-D standard does not define what it is. Better leave it alone */ scc->softr[SCC_CHANNEL_B].wr5 = SCC_WR5_RTS; #else scc->softr[SCC_CHANNEL_B].wr5 = 0; #endif scc->softr[SCC_CHANNEL_A].wr5 = SCC_WR5_RTS | SCC_WR5_DTR; /* baud rates */ val = SCC_WR14_BAUDR_ENABLE|SCC_WR14_BAUDR_SRC; scc->softr[SCC_CHANNEL_B].wr14 = val; scc->softr[SCC_CHANNEL_A].wr14 = val; /* interrupt conditions */ val = SCC_WR1_RXI_ALL_CHAR | SCC_WR1_PARITY_IE | SCC_WR1_EXT_IE | SCC_WR1_TX_IE; scc->softr[SCC_CHANNEL_A].wr1 = val; scc->softr[SCC_CHANNEL_B].wr1 = val; scc_read_reg_zero(regs, SCC_CHANNEL_A, scc->last_rr0[SCC_CHANNEL_A]); scc_read_reg_zero(regs, SCC_CHANNEL_B, scc->last_rr0[SCC_CHANNEL_B]); /* * After probing, any line that should be active * (keybd,mouse,rcline) is activated via scc_param(). */ scc_set_modem_control(scc, scc->full_modem); #if defined(KMIN) || defined (FLAMINGO) || defined(KN03) /* * Crock: MI code knows of unit 0 as console, we need * unit 1 as well since the keyboard is there * This is acceptable on maxine, which has to call its * only one chip unit 1 so that rconsole is happy. */ if (sccunit == 0) { struct bus_device d; d = *ui; d.unit = 1; scc_probe( xxx, &d); } #endif return 1; } boolean_t scc_timer_started = FALSE; static void scc_attach( register struct bus_device *ui) { int sccunit = ui->unit; extern scc_scan(); extern int tty_inq_size; int i; /* We only have 4 ttys, but always at 9600 * Give em a lot of room (plus dma..) */ tty_inq_size = 4096; if (!scc_timer_started) { /* do all of them, before we call scc_scan() */ /* harmless if done already */ for (i = 0; i < NSCC*NSCC_LINE; i++) ttychars(console_tty[i]); scc_timer_started = TRUE; scc_scan(); } #if NBM > 0 if (SCREEN_ISA_CONSOLE() && scc_softc[sccunit]->isa_console) { printf("\n sl0: "); if (sccunit && rcline == 3) printf("( rconsole )"); if (sccunit == SCC_KBDUNIT) { printf("\n sl1: "); lk201_attach(0, sccunit >> 1); } else if (sccunit == SCC_PTRUNIT) { printf("\n sl1: "); mouse_attach(0, sccunit >> 1); } } else #endif /*NBM > 0*/ { printf("%s", (sccunit == 1) ? "\n sl0: ( alternate console )\n sl1:" : "\n sl0:\n sl1:"); } } /* * Would you like to make a phone call ? */ scc_set_modem_control( scc_softc_t scc, boolean_t on) { if (on) /* your problem if the hardware then is broke */ scc->fake = 0; else scc->fake = 3; scc->full_modem = on; /* user should do an scc_param() ifchanged */ } /* * Polled I/O (debugger) */ scc_pollc( int unit, boolean_t on) { scc_softc_t scc; int line = SCREEN_LINE_KEYBOARD, sccunit = unit; mi_to_scc(&sccunit, &line); scc = scc_softc[sccunit]; if (on) { scc->polling_mode++; #if NBM > 0 screen_on_off(unit, TRUE); #endif NBM > 0 } else scc->polling_mode--; } /* * Interrupt routine */ int scc_intr_count; scc_intr( int unit, spl_t spllevel) { scc_softc_t scc = scc_softc[unit]; register scc_regmap_t *regs = scc->regs; register int rr1, rr2; register int c; scc_intr_count++; #if mips splx(spllevel); /* lower priority */ #endif while (1) { scc_read_reg(regs, SCC_CHANNEL_B, SCC_RR2, rr2); rr2 = SCC_RR2_STATUS(rr2); /* are we done yet ? */ if (rr2 == 6) { /* strange, distinguished value */ register int rr3; scc_read_reg(regs, SCC_CHANNEL_A, SCC_RR3, rr3); if (rr3 == 0) return; } if ((rr2 == SCC_RR2_A_XMIT_DONE) || (rr2 == SCC_RR2_B_XMIT_DONE)) { register chan = (rr2 == SCC_RR2_A_XMIT_DONE) ? SCC_CHANNEL_A : SCC_CHANNEL_B; scc_write_reg(regs, SCC_CHANNEL_A, SCC_RR0, SCC_RESET_HIGHEST_IUS); c = cons_simple_tint(scc_to_mi(unit,chan), FALSE); if (c == -1) { /* no more data for this line */ scc_read_reg(regs, chan, SCC_RR15, c); c &= ~SCC_WR15_TX_UNDERRUN_IE; scc_write_reg(regs, chan, SCC_WR15, c); c = scc->softr[chan].wr1 & ~SCC_WR1_TX_IE; scc_write_reg(regs, chan, SCC_WR1, c); scc->softr[chan].wr1 = c; c = cons_simple_tint(scc_to_mi(unit,chan), TRUE); if (c != -1) /* funny race, scc_start has been called already */ scc_write_data(regs, chan, c); } else { scc_write_data(regs, chan, c); /* and leave it enabled */ } } else if (rr2 == SCC_RR2_A_RECV_DONE) { int err = 0; scc_write_reg(regs, SCC_CHANNEL_A, SCC_RR0, SCC_RESET_HIGHEST_IUS); if (scc->polling_mode) continue; scc_read_data(regs, SCC_CHANNEL_A, c); rr1 = scc_to_mi(unit,SCC_CHANNEL_A); cons_simple_rint (rr1, rr1, c, 0); } else if (rr2 == SCC_RR2_B_RECV_DONE) { int err = 0; scc_write_reg(regs, SCC_CHANNEL_A, SCC_RR0, SCC_RESET_HIGHEST_IUS); if (scc->polling_mode) continue; scc_read_data(regs, SCC_CHANNEL_B, c); rr1 = scc_to_mi(unit,SCC_CHANNEL_B); cons_simple_rint (rr1, rr1, c, 0); } else if ((rr2 == SCC_RR2_A_EXT_STATUS) || (rr2 == SCC_RR2_B_EXT_STATUS)) { int chan = (rr2 == SCC_RR2_A_EXT_STATUS) ? SCC_CHANNEL_A : SCC_CHANNEL_B; scc_write_reg(regs, chan, SCC_RR0, SCC_RESET_EXT_IP); scc_write_reg(regs, SCC_CHANNEL_A, SCC_RR0, SCC_RESET_HIGHEST_IUS); scc_modem_intr(scc, chan, unit); } else if ((rr2 == SCC_RR2_A_RECV_SPECIAL) || (rr2 == SCC_RR2_B_RECV_SPECIAL)) { register int chan = (rr2 == SCC_RR2_A_RECV_SPECIAL) ? SCC_CHANNEL_A : SCC_CHANNEL_B; scc_read_reg(regs, chan, SCC_RR1, rr1); if (rr1 & (SCC_RR1_PARITY_ERR | SCC_RR1_RX_OVERRUN | SCC_RR1_FRAME_ERR)) { int err; /* map to CONS_ERR_xxx MI error codes */ err = ((rr1 & SCC_RR1_PARITY_ERR)<<8) | ((rr1 & SCC_RR1_RX_OVERRUN)<<9) | ((rr1 & SCC_RR1_FRAME_ERR)<<7); scc_write_reg(regs, chan, SCC_RR0, SCC_RESET_ERROR); rr1 = scc_to_mi(unit,chan); cons_simple_rint(rr1, rr1, 0, err); } scc_write_reg(regs, SCC_CHANNEL_A, SCC_RR0, SCC_RESET_HIGHEST_IUS); } } } boolean_t scc_start( struct tty *tp) { register scc_regmap_t *regs; register int chan, temp; register struct softreg *sr; temp = (natural_t)tp->t_addr; chan = (temp & 1); /* channel */ temp = (temp >> 1)&0xff;/* sccunit */ regs = scc_softc[temp]->regs; sr = &scc_softc[temp]->softr[chan]; scc_read_reg(regs, chan, SCC_RR15, temp); temp |= SCC_WR15_TX_UNDERRUN_IE; scc_write_reg(regs, chan, SCC_WR15, temp); temp = sr->wr1 | SCC_WR1_TX_IE; scc_write_reg(regs, chan, SCC_WR1, temp); sr->wr1 = temp; /* but we need a first char out or no cookie */ scc_read_reg(regs, chan, SCC_RR0, temp); if (temp & SCC_RR0_TX_EMPTY) { register char c; c = getc(&tp->t_outq); scc_write_data(regs, chan, c); } } /* * Get a char from a specific SCC line * [this is only used for console&screen purposes] */ scc_getc( int unit, int line, boolean_t wait, boolean_t raw) { scc_softc_t scc; register scc_regmap_t *regs; unsigned char c; int value, mi_line, rcvalue, from_line; mi_line = line; mi_to_scc(&unit, &line); scc = scc_softc[unit]; regs = scc->regs; /* * wait till something available * * NOTE: we know! that rcline==3 */ if (rcline) rcline = 3; again: rcvalue = 0; while (1) { scc_read_reg_zero(regs, line, value); if (rcline && (mi_line == SCREEN_LINE_KEYBOARD)) { scc_read_reg_zero(regs, SCC_CHANNEL_B, rcvalue); value |= rcvalue; } if (((value & SCC_RR0_RX_AVAIL) == 0) && wait) delay(10); else break; } /* * if nothing found return -1 */ from_line = (rcvalue & SCC_RR0_RX_AVAIL) ? SCC_CHANNEL_B : line; if (value & SCC_RR0_RX_AVAIL) { scc_read_reg(regs, from_line, SCC_RR1, value); scc_read_data(regs, from_line, c); } else { /* splx(s);*/ return -1; } /* * bad chars not ok */ if (value&(SCC_RR1_PARITY_ERR | SCC_RR1_RX_OVERRUN | SCC_RR1_FRAME_ERR)) { /* scc_state(unit,from_line); */ scc_write_reg(regs, from_line, SCC_RR0, SCC_RESET_ERROR); if (wait) { scc_write_reg(regs, SCC_CHANNEL_A, SCC_RR0, SCC_RESET_HIGHEST_IUS); goto again; } } scc_write_reg(regs, SCC_CHANNEL_A, SCC_RR0, SCC_RESET_HIGHEST_IUS); /* splx(s);*/ #if NBM > 0 if ((mi_line == SCREEN_LINE_KEYBOARD) && (from_line == SCC_CHANNEL_A) && !raw && SCREEN_ISA_CONSOLE() && scc->isa_console) return lk201_rint(SCREEN_CONS_UNIT(), c, wait, scc->polling_mode); else #endif NBM > 0 return c; } /* * Put a char on a specific SCC line */ scc_putc( int unit, int line, int c) { scc_softc_t scc; register scc_regmap_t *regs; spl_t s = spltty(); register int value; mi_to_scc(&unit, &line); scc = scc_softc[unit]; regs = scc->regs; do { scc_read_reg(regs, line, SCC_RR0, value); if (value & SCC_RR0_TX_EMPTY) break; delay(100); } while (1); scc_write_data(regs, line, c); /* wait for it to swallow the char ? */ splx(s); } scc_param( struct tty *tp, int line) { scc_regmap_t *regs; int value, sccline, unit; struct softreg *sr; scc_softc_t scc; line = tp->t_dev; /* MI code wants us to handle 4 lines on unit 0 */ unit = (line < 4) ? 0 : (line / NSCC_LINE); sccline = line; mi_to_scc(&unit, &sccline); if ((scc = scc_softc[unit]) == 0) return; /* sanity */ regs = scc->regs; sr = &scc->softr[sccline]; /* * Do not let user fool around with kbd&mouse */ #if NBM > 0 if (screen_captures(line)) { tp->t_ispeed = tp->t_ospeed = B4800; tp->t_flags |= TF_LITOUT; } #endif NBM > 0 if (tp->t_ispeed == 0) { (void) scc_mctl(tp->t_dev, TM_HUP, DMSET); /* hang up line */ return; } /* reset line */ value = (sccline == SCC_CHANNEL_A) ? SCC_WR9_RESET_CHA_A : SCC_WR9_RESET_CHA_B; scc_write_reg(regs, sccline, SCC_WR9, value); delay(25); /* stop bits, normally 1 */ value = sr->wr4 & 0xf0; value |= (tp->t_ispeed == B110) ? SCC_WR4_2_STOP : SCC_WR4_1_STOP; /* .. and parity */ if ((tp->t_flags & (TF_ODDP | TF_EVENP)) == TF_ODDP) value |= SCC_WR4_PARITY_ENABLE; /* set it now, remember it must be first after reset */ sr->wr4 = value; scc_write_reg(regs, sccline, SCC_WR4, value); /* vector again */ scc_write_reg(regs, sccline, SCC_WR2, 0xf0); /* we only do 8 bits per char */ value = SCC_WR3_RX_8_BITS; scc_write_reg(regs, sccline, SCC_WR3, value); /* clear break, keep rts dtr */ value = sr->wr5 & (SCC_WR5_DTR|SCC_WR5_RTS); value |= SCC_WR5_TX_8_BITS; sr->wr5 = value; scc_write_reg(regs, sccline, SCC_WR5, value); /* some are on the other channel, which might never be used (e.g. maxine has only one line) */ { register int otherline = (sccline+1)&1; scc_write_reg(regs, otherline, SCC_WR5, scc->softr[otherline].wr5); } scc_write_reg(regs, sccline, SCC_WR6, 0); scc_write_reg(regs, sccline, SCC_WR7, 0); scc_write_reg(regs, sccline, SCC_WR9, SCC_WR9_VIS); scc_write_reg(regs, sccline, SCC_WR10, 0); /* clock config */ value = SCC_WR11_RCLK_BAUDR | SCC_WR11_XTLK_BAUDR | SCC_WR11_TRc_OUT | SCC_WR11_TRcOUT_BAUDR; scc_write_reg(regs, sccline, SCC_WR11, value); value = scc_speeds[tp->t_ispeed]; scc_set_timing_base(regs,sccline,value); value = sr->wr14; scc_write_reg(regs, sccline, SCC_WR14, value); #if FLAMINGO if (unit != 1) #else if (1) #endif { /* Chan-A: CTS==SI DCD==RI DSR=SYNCH */ value = SCC_WR15_CTS_IE | SCC_WR15_DCD_IE | SCC_WR15_SYNCHUNT_IE; scc_write_reg(regs, SCC_CHANNEL_A, SCC_WR15, value); /* Chan-B: CTS==CTS DCD==DCD */ value = SCC_WR15_BREAK_IE | SCC_WR15_CTS_IE | SCC_WR15_DCD_IE; scc_write_reg(regs, SCC_CHANNEL_B, SCC_WR15, value); } else { /* Here if modem bits are floating noise, keep quiet */ value = SCC_WR15_BREAK_IE; scc_write_reg(regs, sccline, SCC_WR15, value); } /* and now the enables */ value = SCC_WR3_RX_8_BITS | SCC_WR3_RX_ENABLE; scc_write_reg(regs, sccline, SCC_WR3, value); value = sr->wr5 | SCC_WR5_TX_ENABLE; sr->wr5 = value; scc_write_reg(regs, sccline, SCC_WR5, value); /* master inter enable */ scc_write_reg(regs,sccline,SCC_WR9,SCC_WR9_MASTER_IE|SCC_WR9_VIS); scc_write_reg(regs, sccline, SCC_WR1, sr->wr1); } /* * Modem control functions */ scc_mctl( int dev, int bits, int how) { register scc_regmap_t *regs; struct softreg *sra, *srb, *sr; int unit, sccline; int b = 0; spl_t s; scc_softc_t scc; /* MI code wants us to handle 4 lines on unit 0 */ unit = (dev < 4) ? 0 : (dev / NSCC_LINE); sccline = dev; mi_to_scc(&unit, &sccline); if ((scc = scc_softc[unit]) == 0) return 0; /* sanity */ regs = scc->regs; sr = &scc->softr[sccline]; sra = &scc->softr[SCC_CHANNEL_A]; srb = &scc->softr[SCC_CHANNEL_B]; if (bits == TM_HUP) { /* close line (internal) */ bits = TM_DTR | TM_RTS; how = DMBIC; /* xxx interrupts too ?? */ } if (bits & TM_BRK) { switch (how) { case DMSET: case DMBIS: sr->wr5 |= SCC_WR5_SEND_BREAK; break; case DMBIC: sr->wr5 &= ~SCC_WR5_SEND_BREAK; break; default: goto dontbrk; } s = spltty(); scc_write_reg(regs, sccline, SCC_WR5, sr->wr5); splx(s); dontbrk: b |= (sr->wr5 & SCC_WR5_SEND_BREAK) ? TM_BRK : 0; } /* no modem support on channel A */ if (sccline == SCC_CHANNEL_A) return (b | TM_LE | TM_DTR | TM_CTS | TM_CAR | TM_DSR); sra = &scc->softr[SCC_CHANNEL_A]; srb = &scc->softr[SCC_CHANNEL_B]; #if 0 /* do I need to do something on this ? */ if (bits & TM_LE) { /* line enable */ } #endif if (bits & (TM_DTR|TM_RTS)) { /* data terminal ready, request to send */ register int w = 0; if (bits & TM_DTR) w |= SCC_WR5_DTR; if (bits & TM_RTS) w |= SCC_WR5_RTS; switch (how) { case DMSET: case DMBIS: sra->wr5 |= w; break; case DMBIC: sra->wr5 &= ~w; break; default: goto dontdtr; } s = spltty(); scc_write_reg(regs, SCC_CHANNEL_A, SCC_WR5, sra->wr5); splx(s); dontdtr: b |= (sra->wr5 & w) ? (bits & (TM_DTR|TM_RTS)) : 0; } s = spltty(); #if 0 /* Unsupported */ if (bits & TM_ST) { /* secondary transmit */ } if (bits & TM_SR) { /* secondary receive */ } #endif if (bits & TM_CTS) { /* clear to send */ register int value; scc_read_reg(regs, SCC_CHANNEL_B, SCC_RR0, value); b |= (value & SCC_RR0_CTS) ? TM_CTS : 0; } if (bits & TM_CAR) { /* carrier detect */ register int value; scc_read_reg(regs, SCC_CHANNEL_B, SCC_RR0, value); b |= (value & SCC_RR0_DCD) ? TM_CAR : 0; } if (bits & TM_RNG) { /* ring */ register int value; scc_read_reg(regs, SCC_CHANNEL_A, SCC_RR0, value); b |= (value & SCC_RR0_DCD) ? TM_RNG : 0; } if (bits & TM_DSR) { /* data set ready */ register int value; scc_read_reg(regs, SCC_CHANNEL_A, SCC_RR0, value); b |= (value & SCC_RR0_SYNCH) ? TM_DSR : 0; } splx(s); return b; } #define debug 0 scc_modem_intr( scc_softc_t scc, int chan, int unit) { register int value, changed; scc_read_reg_zero(scc->regs, chan, value); /* See what changed */ changed = value ^ scc->last_rr0[chan]; scc->last_rr0[chan] = value; #if debug printf("sccmodem: chan %c now %x, changed %x : ", (chan == SCC_CHANNEL_B) ? 'B' : 'A', value, changed); #endif if (chan == SCC_CHANNEL_A) { if (changed & SCC_RR0_CTS) { /* Speed indicator, ignore XXX */ #if debug printf("%s-speed ", (value & SCC_RR0_CTS) ? "Full" : "Half"); #endif } if (changed & SCC_RR0_DCD) { /* Ring indicator */ #if debug printf("Ring "); #endif } if (changed & SCC_RR0_SYNCH) { /* Data Set Ready */ #if debug printf("DSR "); #endif /* If modem went down then CD will also go down, or it did already. If modem came up then we have to wait for CD anyways before enabling the line. Either way, nothing to do here */ } } else { if (changed & SCC_RR0_CTS) { /* Clear To Send */ #if debug printf("CTS "); #endif tty_cts(console_tty[scc_to_mi(unit,chan)], value & SCC_RR0_CTS); } if (changed & SCC_RR0_DCD) { #if debug printf("CD "); #endif check_car(console_tty[scc_to_mi(unit,chan)], value & SCC_RR0_DCD); } } #if debug printf(".\n"); #endif } private check_car( register struct tty *tp, boolean_t car) { if (car) { #if notyet /* cancel modem timeout if need to */ if (car & (SCC_MSR_CD2 | SCC_MSR_CD3)) untimeout(scc_hup, (vm_offset_t)tp); #endif /* I think this belongs in the MI code */ if (tp->t_state & TS_WOPEN) tp->t_state |= TS_ISOPEN; /* carrier present */ if ((tp->t_state & TS_CARR_ON) == 0) (void)ttymodem(tp, 1); } else if ((tp->t_state&TS_CARR_ON) && ttymodem(tp, 0) == 0) scc_mctl( tp->t_dev, TM_DTR, DMBIC); } /* * Periodically look at the CD signals: * they do generate interrupts but we * must fake them on channel A. We might * also fake them on channel B. */ scc_scan() { register i; spl_t s = spltty(); for (i = 0; i < NSCC; i++) { register scc_softc_t scc; register int car; register struct tty **tpp; scc = scc_softc[i]; if (scc == 0) continue; car = scc->softCAR | scc->fake; tpp = &console_tty[i * NSCC_LINE]; while (car) { if (car & 1) check_car(*tpp, 1); tpp++; car = car>>1; } } splx(s); timeout(scc_scan, (vm_offset_t)0, 5*hz); } #if debug scc_rr0(unit,chan) { int val; scc_read_reg_zero(scc_softc[unit]->regs, chan, val); return val; } scc_rreg(unit,chan,n) { int val; scc_read_reg(scc_softc[unit]->regs, chan, n, val); return val; } scc_wreg(unit,chan,n,val) { scc_write_reg(scc_softc[unit]->regs, chan, n, val); } scc_state(unit,soft) { int rr0, rr1, rr3, rr12, rr13, rr15; rr0 = scc_rreg(unit, SCC_CHANNEL_A, SCC_RR0); rr1 = scc_rreg(unit, SCC_CHANNEL_A, SCC_RR1); rr3 = scc_rreg(unit, SCC_CHANNEL_A, SCC_RR3); rr12 = scc_rreg(unit, SCC_CHANNEL_A, SCC_RR12); rr13 = scc_rreg(unit, SCC_CHANNEL_A, SCC_RR13); rr15 = scc_rreg(unit, SCC_CHANNEL_A, SCC_RR15); printf("{%d intr, A: R0 %x R1 %x R3 %x baudr %x R15 %x}\n", scc_intr_count, rr0, rr1, rr3, (rr13 << 8) | rr12, rr15); rr0 = scc_rreg(unit, SCC_CHANNEL_B, SCC_RR0); rr1 = scc_rreg(unit, SCC_CHANNEL_B, SCC_RR1); rr3 = scc_rreg(unit, SCC_CHANNEL_B, SCC_RR2); rr12 = scc_rreg(unit, SCC_CHANNEL_B, SCC_RR12); rr13 = scc_rreg(unit, SCC_CHANNEL_B, SCC_RR13); rr15 = scc_rreg(unit, SCC_CHANNEL_B, SCC_RR15); printf("{B: R0 %x R1 %x R2 %x baudr %x R15 %x}\n", rr0, rr1, rr3, (rr13 << 8) | rr12, rr15); if (soft) { struct softreg *sr; sr = scc_softc[unit]->softr; printf("{B: W1 %x W4 %x W5 %x W14 %x}", sr->wr1, sr->wr4, sr->wr5, sr->wr14); sr++; printf("{A: W1 %x W4 %x W5 %x W14 %x}\n", sr->wr1, sr->wr4, sr->wr5, sr->wr14); } } #endif #endif NSCC > 0