141480Smckusick /* 241480Smckusick * Copyright (c) 1988 University of Utah. 341480Smckusick * Copyright (c) 1982, 1986, 1990 The Regents of the University of California. 441480Smckusick * All rights reserved. 541480Smckusick * 641480Smckusick * This code is derived from software contributed to Berkeley by 741480Smckusick * the Systems Programming Group of the University of Utah Computer 841480Smckusick * Science Department. 941480Smckusick * 1041480Smckusick * %sccs.include.redist.c% 1141480Smckusick * 1241480Smckusick * from: $Hdr: dcm.c 1.17 89/10/01$ 1341480Smckusick * 14*43407Shibler * @(#)dcm.c 7.5 (Berkeley) 06/22/90 1541480Smckusick */ 1641480Smckusick 1741480Smckusick /* 1842354Smckusick * TODO: 1942354Smckusick * Timeouts 2042354Smckusick * Test console/kgdb support. 2141480Smckusick */ 2241480Smckusick 2341480Smckusick #include "dcm.h" 2441480Smckusick #if NDCM > 0 2541480Smckusick /* 2641480Smckusick * 98642/MUX 2741480Smckusick */ 2841480Smckusick #include "param.h" 2941480Smckusick #include "systm.h" 3041480Smckusick #include "ioctl.h" 3141480Smckusick #include "tty.h" 3241480Smckusick #include "user.h" 3341480Smckusick #include "conf.h" 3441480Smckusick #include "file.h" 3541480Smckusick #include "uio.h" 3641480Smckusick #include "kernel.h" 3741480Smckusick #include "syslog.h" 3841480Smckusick #include "time.h" 3941480Smckusick 4041480Smckusick #include "device.h" 4141480Smckusick #include "dcmreg.h" 4241480Smckusick #include "machine/cpu.h" 4341480Smckusick #include "machine/isr.h" 4441480Smckusick 4542354Smckusick #ifndef DEFAULT_BAUD_RATE 4642354Smckusick #define DEFAULT_BAUD_RATE 9600 4742354Smckusick #endif 4842354Smckusick 4942354Smckusick int ttrstrt(); 5042354Smckusick int dcmprobe(), dcmstart(), dcmintr(), dcmparam(); 5142354Smckusick 5241480Smckusick struct driver dcmdriver = { 5341480Smckusick dcmprobe, "dcm", 5441480Smckusick }; 5541480Smckusick 5641480Smckusick #define NDCMLINE (NDCM*4) 5741480Smckusick 5842354Smckusick struct tty dcm_tty[NDCMLINE]; 5942354Smckusick int ndcm = NDCMLINE; 6042354Smckusick 6142354Smckusick int dcm_active; 6241480Smckusick int dcmsoftCAR[NDCM]; 6341480Smckusick struct dcmdevice *dcm_addr[NDCM]; 6441480Smckusick struct isr dcmisr[NDCM]; 6541480Smckusick 6641480Smckusick struct speedtab dcmspeedtab[] = { 6741480Smckusick 0, BR_0, 6841480Smckusick 50, BR_50, 6941480Smckusick 75, BR_75, 7041480Smckusick 110, BR_110, 7141480Smckusick 134, BR_134, 7241480Smckusick 150, BR_150, 7341480Smckusick 300, BR_300, 7441480Smckusick 600, BR_600, 7541480Smckusick 1200, BR_1200, 7641480Smckusick 1800, BR_1800, 7741480Smckusick 2400, BR_2400, 7841480Smckusick 4800, BR_4800, 7941480Smckusick 9600, BR_9600, 8041480Smckusick 19200, BR_19200, 8141480Smckusick 38400, BR_38400, 8241480Smckusick -1, -1 8341480Smckusick }; 8441480Smckusick 8542354Smckusick /* u-sec per character based on baudrate (assumes 1 start/8 data/1 stop bit) */ 8642354Smckusick #define DCM_USPERCH(s) (10000000 / (s)) 8742354Smckusick 8842354Smckusick /* 8942354Smckusick * Per board interrupt scheme. 16.7ms is the polling interrupt rate 9042354Smckusick * (16.7ms is about 550 buad, 38.4k is 72 chars in 16.7ms). 9142354Smckusick */ 9242354Smckusick #define DIS_TIMER 0 9342354Smckusick #define DIS_PERCHAR 1 9442354Smckusick #define DIS_RESET 2 9542354Smckusick 9642354Smckusick int dcmistype = -1; /* -1 == dynamic, 0 == timer, 1 == perchar */ 9742354Smckusick int dcminterval = 5; /* interval (secs) between checks */ 9842354Smckusick struct dcmischeme { 9942354Smckusick int dis_perchar; /* non-zero if interrupting per char */ 10042354Smckusick long dis_time; /* last time examined */ 10142354Smckusick int dis_intr; /* recv interrupts during last interval */ 10242354Smckusick int dis_char; /* characters read during last interval */ 10342354Smckusick } dcmischeme[NDCM]; 10442354Smckusick 10542354Smckusick /* 10642354Smckusick * Console support 10742354Smckusick */ 10842354Smckusick int dcmconsole = -1; 10942354Smckusick int dcmdefaultrate = DEFAULT_BAUD_RATE; 11042354Smckusick int dcmconbrdbusy = 0; 11142354Smckusick extern struct tty *constty; 11242354Smckusick 11342354Smckusick #ifdef KGDB 11442354Smckusick /* 11542354Smckusick * Kernel GDB support 11642354Smckusick */ 11742354Smckusick extern int kgdb_dev; 11842354Smckusick extern int kgdb_rate; 11942354Smckusick extern int kgdb_debug_init; 12042354Smckusick #endif 12142354Smckusick 12242354Smckusick /* #define IOSTATS */ 12342354Smckusick 12441480Smckusick #ifdef DEBUG 12541480Smckusick int dcmdebug = 0x00; 12641480Smckusick #define DDB_SIOERR 0x01 12741480Smckusick #define DDB_PARAM 0x02 12841480Smckusick #define DDB_INPUT 0x04 12941480Smckusick #define DDB_OUTPUT 0x08 13041480Smckusick #define DDB_INTR 0x10 13142354Smckusick #define DDB_IOCTL 0x20 13242354Smckusick #define DDB_INTSCHM 0x40 13342354Smckusick #define DDB_MODEM 0x80 13441480Smckusick #define DDB_OPENCLOSE 0x100 13542354Smckusick #endif 13641480Smckusick 13742354Smckusick #ifdef IOSTATS 13842354Smckusick #define DCMRBSIZE 94 13942354Smckusick #define DCMXBSIZE 24 14042354Smckusick 14142354Smckusick struct dcmstats { 14242354Smckusick long xints; /* # of xmit ints */ 14342354Smckusick long xchars; /* # of xmit chars */ 14442354Smckusick long xempty; /* times outq is empty in dcmstart */ 14542354Smckusick long xrestarts; /* times completed while xmitting */ 14642354Smckusick long rints; /* # of recv ints */ 14742354Smckusick long rchars; /* # of recv chars */ 14842354Smckusick long xsilo[DCMXBSIZE+2]; /* times this many chars xmit on one int */ 14942354Smckusick long rsilo[DCMRBSIZE+2]; /* times this many chars read on one int */ 15042354Smckusick } dcmstats[NDCM]; 15141480Smckusick #endif 15241480Smckusick 15341480Smckusick #define UNIT(x) minor(x) 15442354Smckusick #define BOARD(x) (((x) >> 2) & 0x3f) 15541480Smckusick #define PORT(x) ((x) & 3) 15641480Smckusick #define MKUNIT(b,p) (((b) << 2) | (p)) 15741480Smckusick 15841480Smckusick dcmprobe(hd) 15941480Smckusick register struct hp_device *hd; 16041480Smckusick { 16141480Smckusick register struct dcmdevice *dcm; 16241480Smckusick register int i; 16341480Smckusick register int timo = 0; 16442354Smckusick int s, brd, isconsole; 16541480Smckusick 16641480Smckusick dcm = (struct dcmdevice *)hd->hp_addr; 16741480Smckusick if ((dcm->dcm_rsid & 0x1f) != DCMID) 16841480Smckusick return (0); 16941480Smckusick brd = hd->hp_unit; 17042354Smckusick isconsole = (brd == BOARD(dcmconsole)); 17142354Smckusick /* 17242354Smckusick * XXX selected console device (CONSUNIT) as determined by 17342354Smckusick * dcmcnprobe does not agree with logical numbering imposed 17442354Smckusick * by the config file (i.e. lowest address DCM is not unit 17542354Smckusick * CONSUNIT). Don't recognize this card. 17642354Smckusick */ 17742354Smckusick if (isconsole && dcm != dcm_addr[BOARD(dcmconsole)]) 17842354Smckusick return(0); 17942354Smckusick 18042354Smckusick /* 18142354Smckusick * Empirically derived self-test magic 18242354Smckusick */ 18341480Smckusick s = spltty(); 18441480Smckusick dcm->dcm_rsid = DCMRS; 18541480Smckusick DELAY(50000); /* 5000 is not long enough */ 18641480Smckusick dcm->dcm_rsid = 0; 18741480Smckusick dcm->dcm_ic = IC_IE; 18841480Smckusick dcm->dcm_cr = CR_SELFT; 18942354Smckusick while ((dcm->dcm_ic & IC_IR) == 0) 19042354Smckusick if (++timo == 20000) 19141480Smckusick return(0); 19241480Smckusick DELAY(50000) /* XXX why is this needed ???? */ 19342354Smckusick while ((dcm->dcm_iir & IIR_SELFT) == 0) 19442354Smckusick if (++timo == 400000) 19541480Smckusick return(0); 19641480Smckusick DELAY(50000) /* XXX why is this needed ???? */ 19741480Smckusick if (dcm->dcm_stcon != ST_OK) { 19842354Smckusick if (!isconsole) 19942354Smckusick printf("dcm%d: self test failed: %x\n", 20042354Smckusick brd, dcm->dcm_stcon); 20141480Smckusick return(0); 20241480Smckusick } 20341480Smckusick dcm->dcm_ic = IC_ID; 20441480Smckusick splx(s); 20541480Smckusick 20641480Smckusick hd->hp_ipl = DCMIPL(dcm->dcm_ic); 20742354Smckusick dcm_addr[brd] = dcm; 20842354Smckusick dcm_active |= 1 << brd; 20942354Smckusick dcmsoftCAR[brd] = hd->hp_flags; 21041480Smckusick dcmisr[brd].isr_ipl = hd->hp_ipl; 21141480Smckusick dcmisr[brd].isr_arg = brd; 21241480Smckusick dcmisr[brd].isr_intr = dcmintr; 21341480Smckusick isrlink(&dcmisr[brd]); 21442354Smckusick #ifdef KGDB 21542354Smckusick if (major(kgdb_dev) == 2 && BOARD(kgdb_dev) == brd) { 21642354Smckusick if (dcmconsole == UNIT(kgdb_dev)) 21742354Smckusick kgdb_dev = -1; /* can't debug over console port */ 21842354Smckusick else { 21942354Smckusick (void) dcminit(kgdb_dev, kgdb_rate); 22042354Smckusick if (kgdb_debug_init) { 22142354Smckusick printf("dcm%d: kgdb waiting...", 22242354Smckusick UNIT(kgdb_dev)); 22342354Smckusick /* trap into kgdb */ 22442354Smckusick asm("trap #15;"); 22542354Smckusick printf("connected.\n"); 22642354Smckusick } else 22742354Smckusick printf("dcm%d: kgdb enabled\n", 22842354Smckusick UNIT(kgdb_dev)); 22942354Smckusick } 23042354Smckusick } 23142354Smckusick #endif 23242354Smckusick if (dcmistype == DIS_TIMER) 23342354Smckusick dcmsetischeme(brd, DIS_RESET|DIS_TIMER); 23442354Smckusick else 23542354Smckusick dcmsetischeme(brd, DIS_RESET|DIS_PERCHAR); 23642354Smckusick dcm->dcm_mdmmsk = MI_CD; /* enable modem carrier detect intr */ 23742354Smckusick dcm->dcm_ic = IC_IE; /* turn all interrupts on */ 23841480Smckusick /* 23941480Smckusick * Need to reset baud rate, etc. of next print so reset dcmconsole. 24041480Smckusick * Also make sure console is always "hardwired" 24141480Smckusick */ 24242354Smckusick if (isconsole) { 24342354Smckusick dcmconsole = -1; 24441480Smckusick dcmsoftCAR[brd] |= (1 << PORT(dcmconsole)); 24541480Smckusick } 24641480Smckusick return (1); 24741480Smckusick } 24841480Smckusick 24941480Smckusick dcmopen(dev, flag) 25041480Smckusick dev_t dev; 25141480Smckusick { 25241480Smckusick register struct tty *tp; 25341480Smckusick register int unit, brd; 25442354Smckusick int error; 25541480Smckusick 25641480Smckusick unit = UNIT(dev); 25741480Smckusick brd = BOARD(unit); 25842354Smckusick if (unit >= NDCMLINE || (dcm_active & (1 << brd)) == 0) 25941480Smckusick return (ENXIO); 26042354Smckusick #ifdef KGDB 26142354Smckusick if (unit == UNIT(kgdb_dev)) 26242354Smckusick return (EBUSY); 26342354Smckusick #endif 26441480Smckusick tp = &dcm_tty[unit]; 26541480Smckusick tp->t_oproc = dcmstart; 26642354Smckusick tp->t_param = dcmparam; 26741480Smckusick tp->t_dev = dev; 26841480Smckusick if ((tp->t_state & TS_ISOPEN) == 0) { 26942950Smarc tp->t_state |= TS_WOPEN; 27041480Smckusick ttychars(tp); 27141480Smckusick tp->t_iflag = TTYDEF_IFLAG; 27241480Smckusick tp->t_oflag = TTYDEF_OFLAG; 27341480Smckusick tp->t_cflag = TTYDEF_CFLAG; 27441480Smckusick tp->t_lflag = TTYDEF_LFLAG; 27541480Smckusick tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 27642354Smckusick (void) dcmparam(tp, &tp->t_termios); 27741480Smckusick ttsetwater(tp); 27841480Smckusick } else if (tp->t_state&TS_XCLUDE && u.u_uid != 0) 27941480Smckusick return (EBUSY); 28041480Smckusick if (PORT(unit) == 0) /* enable port 0 */ 28141480Smckusick (void) dcmmctl(dev, MO_ON, DMSET); 28241480Smckusick if (dcmsoftCAR[brd] & (1 << PORT(unit))) 28341480Smckusick tp->t_state |= TS_CARR_ON; 28441480Smckusick else if (PORT(unit)) /* Only port 0 has modem control */ 28541480Smckusick tp->t_state |= TS_CARR_ON; 28641480Smckusick else if (dcmmctl(dev, MO_OFF, DMGET) & MI_CD) 28741480Smckusick tp->t_state |= TS_CARR_ON; 28841480Smckusick (void) spltty(); 28941480Smckusick while (!(flag&O_NONBLOCK) && !(tp->t_cflag&CLOCAL) && 29041480Smckusick (tp->t_state & TS_CARR_ON) == 0) { 29141480Smckusick tp->t_state |= TS_WOPEN; 29242946Smarc if ((error = tsleep((caddr_t)&tp->t_rawq, TTIPRI | PCATCH, 29342946Smarc ttopen, 0)) || 29442946Smarc (error = ttclosed(tp))) { 29542354Smckusick tp->t_state &= ~TS_WOPEN; 29642354Smckusick (void) spl0(); 29742354Smckusick return (error); 29842354Smckusick } 29941480Smckusick } 30041480Smckusick (void) spl0(); 30141480Smckusick 30241480Smckusick #ifdef DEBUG 30341480Smckusick if (dcmdebug & DDB_OPENCLOSE) 30441480Smckusick printf("dcmopen: u %x st %x fl %x\n", 30541480Smckusick unit, tp->t_state, tp->t_flags); 30641480Smckusick #endif 30741480Smckusick return ((*linesw[tp->t_line].l_open)(dev, tp)); 30841480Smckusick } 30941480Smckusick 31041480Smckusick /*ARGSUSED*/ 31141480Smckusick dcmclose(dev, flag) 31241480Smckusick dev_t dev; 31341480Smckusick { 31441480Smckusick register struct tty *tp; 31541480Smckusick int unit; 31641480Smckusick 31741480Smckusick unit = UNIT(dev); 31841480Smckusick tp = &dcm_tty[unit]; 31941480Smckusick (*linesw[tp->t_line].l_close)(tp); 32041480Smckusick if (tp->t_cflag&HUPCL || tp->t_state&TS_WOPEN || 32141480Smckusick (tp->t_state&TS_ISOPEN) == 0) 32241480Smckusick (void) dcmmctl(dev, MO_OFF, DMSET); 32341480Smckusick #ifdef DEBUG 32441480Smckusick if (dcmdebug & DDB_OPENCLOSE) 32541480Smckusick printf("dcmclose: u %x st %x fl %x\n", 32641480Smckusick unit, tp->t_state, tp->t_flags); 32741480Smckusick #endif 32841480Smckusick ttyclose(tp); 32941480Smckusick return(0); 33041480Smckusick } 33141480Smckusick 33241480Smckusick dcmread(dev, uio, flag) 33341480Smckusick dev_t dev; 33441480Smckusick struct uio *uio; 33541480Smckusick { 33641480Smckusick register struct tty *tp; 33741480Smckusick 33841480Smckusick tp = &dcm_tty[UNIT(dev)]; 33941480Smckusick return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); 34041480Smckusick } 34141480Smckusick 34241480Smckusick dcmwrite(dev, uio, flag) 34341480Smckusick dev_t dev; 34441480Smckusick struct uio *uio; 34541480Smckusick { 34641480Smckusick int unit = UNIT(dev); 34741480Smckusick register struct tty *tp; 34841480Smckusick 34941480Smckusick tp = &dcm_tty[unit]; 35042354Smckusick /* 35142354Smckusick * XXX we disallow virtual consoles if the physical console is 35242354Smckusick * a serial port. This is in case there is a display attached that 35342354Smckusick * is not the console. In that situation we don't need/want the X 35442354Smckusick * server taking over the console. 35542354Smckusick */ 35642354Smckusick if (constty && unit == dcmconsole) 35742354Smckusick constty = NULL; 35841480Smckusick return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); 35941480Smckusick } 36041480Smckusick 36141480Smckusick dcmintr(brd) 36241480Smckusick register int brd; 36341480Smckusick { 36442354Smckusick register struct dcmdevice *dcm = dcm_addr[brd]; 36542354Smckusick register struct dcmischeme *dis; 36641480Smckusick int i, code, pcnd[4], mcnd, delta; 36741480Smckusick 36842354Smckusick /* 36942354Smckusick * Do all guarded register accesses right off to minimize 37042354Smckusick * block out of hardware. 37142354Smckusick */ 37241480Smckusick SEM_LOCK(dcm); 37341480Smckusick if ((dcm->dcm_ic & IC_IR) == 0) { 37441480Smckusick SEM_UNLOCK(dcm); 37541480Smckusick return(0); 37641480Smckusick } 37741480Smckusick for (i = 0; i < 4; i++) { 37841480Smckusick pcnd[i] = dcm->dcm_icrtab[i].dcm_data; 37941480Smckusick dcm->dcm_icrtab[i].dcm_data = 0; 38041480Smckusick } 38141480Smckusick mcnd = dcm->dcm_mdmin; 38241480Smckusick code = dcm->dcm_iir & IIR_MASK; 38342354Smckusick dcm->dcm_iir = 0; /* XXX doc claims read clears interrupt?! */ 38441480Smckusick SEM_UNLOCK(dcm); 38541480Smckusick 38641480Smckusick #ifdef DEBUG 38741480Smckusick if (dcmdebug & DDB_INTR) 38842354Smckusick printf("dcmintr(%d): iir %x p0 %x p1 %x p2 %x p3 %x m %x\n", 38942354Smckusick brd, code, pcnd[0], pcnd[1], pcnd[2], pcnd[3], mcnd); 39041480Smckusick #endif 39142354Smckusick if (code & IIR_TIMEO) 39242354Smckusick dcmrint(brd, dcm); 39341480Smckusick if (code & IIR_PORT0) 39441480Smckusick dcmpint(MKUNIT(brd, 0), pcnd[0], dcm); 39541480Smckusick if (code & IIR_PORT1) 39641480Smckusick dcmpint(MKUNIT(brd, 1), pcnd[1], dcm); 39741480Smckusick if (code & IIR_PORT2) 39841480Smckusick dcmpint(MKUNIT(brd, 2), pcnd[2], dcm); 39941480Smckusick if (code & IIR_PORT3) 40041480Smckusick dcmpint(MKUNIT(brd, 3), pcnd[3], dcm); 40141480Smckusick if (code & IIR_MODM) 40241480Smckusick dcmmint(MKUNIT(brd, 0), mcnd, dcm); /* always port 0 */ 40341480Smckusick 40442354Smckusick dis = &dcmischeme[brd]; 40541480Smckusick /* 40642354Smckusick * Chalk up a receiver interrupt if the timer running or one of 40742354Smckusick * the ports reports a special character interrupt. 40841480Smckusick */ 40942354Smckusick if ((code & IIR_TIMEO) || 41042354Smckusick ((pcnd[0]|pcnd[1]|pcnd[2]|pcnd[3]) & IT_SPEC)) 41142354Smckusick dis->dis_intr++; 41242354Smckusick /* 41342354Smckusick * See if it is time to check/change the interrupt rate. 41442354Smckusick */ 41542354Smckusick if (dcmistype < 0 && 41642354Smckusick (delta = time.tv_sec - dis->dis_time) >= dcminterval) { 41741480Smckusick /* 41842354Smckusick * If currently per-character and averaged over 70 interrupts 41942354Smckusick * per-second (66 is threshold of 600 baud) in last interval, 42042354Smckusick * switch to timer mode. 42142354Smckusick * 42242354Smckusick * XXX decay counts ala load average to avoid spikes? 42341480Smckusick */ 42442354Smckusick if (dis->dis_perchar && dis->dis_intr > 70 * delta) 42542354Smckusick dcmsetischeme(brd, DIS_TIMER); 42642354Smckusick /* 42742354Smckusick * If currently using timer and had more interrupts than 42842354Smckusick * received characters in the last interval, switch back 42942354Smckusick * to per-character. Note that after changing to per-char 43042354Smckusick * we must process any characters already in the queue 43142354Smckusick * since they may have arrived before the bitmap was setup. 43242354Smckusick * 43342354Smckusick * XXX decay counts? 43442354Smckusick */ 43542354Smckusick else if (!dis->dis_perchar && dis->dis_intr > dis->dis_char) { 43642354Smckusick dcmsetischeme(brd, DIS_PERCHAR); 43741480Smckusick dcmrint(brd, dcm); 43841480Smckusick } 43942354Smckusick dis->dis_intr = dis->dis_char = 0; 44042354Smckusick dis->dis_time = time.tv_sec; 44142354Smckusick } 44241480Smckusick return(1); 44341480Smckusick } 44441480Smckusick 44541480Smckusick /* 44641480Smckusick * Port interrupt. Can be two things: 44741480Smckusick * First, it might be a special character (exception interrupt); 44841480Smckusick * Second, it may be a buffer empty (transmit interrupt); 44941480Smckusick */ 45041480Smckusick dcmpint(unit, code, dcm) 45141480Smckusick int unit, code; 45242354Smckusick struct dcmdevice *dcm; 45341480Smckusick { 45442354Smckusick struct tty *tp = &dcm_tty[unit]; 45541480Smckusick 45642354Smckusick if (code & IT_SPEC) 45742354Smckusick dcmreadbuf(unit, dcm, tp); 45841480Smckusick if (code & IT_TX) 45942354Smckusick dcmxint(unit, dcm, tp); 46041480Smckusick } 46141480Smckusick 46241480Smckusick dcmrint(brd, dcm) 46341480Smckusick int brd; 46441480Smckusick register struct dcmdevice *dcm; 46541480Smckusick { 46642354Smckusick register int i, unit; 46741480Smckusick register struct tty *tp; 46841480Smckusick 46941480Smckusick unit = MKUNIT(brd, 0); 47041480Smckusick tp = &dcm_tty[unit]; 47142354Smckusick for (i = 0; i < 4; i++, tp++, unit++) 47242354Smckusick dcmreadbuf(unit, dcm, tp); 47341480Smckusick } 47441480Smckusick 47542354Smckusick dcmreadbuf(unit, dcm, tp) 47641480Smckusick int unit; 47741480Smckusick register struct dcmdevice *dcm; 47841480Smckusick register struct tty *tp; 47941480Smckusick { 48042354Smckusick int port = PORT(unit); 48142354Smckusick register struct dcmpreg *pp = dcm_preg(dcm, port); 48242354Smckusick register struct dcmrfifo *fifo; 48341480Smckusick register int c, stat; 48441480Smckusick register unsigned head; 48542354Smckusick int nch = 0; 48642354Smckusick #ifdef IOSTATS 48742354Smckusick struct dcmstats *dsp = &dcmstats[BOARD(unit)]; 48841480Smckusick 48942354Smckusick dsp->rints++; 49042354Smckusick #endif 491*43407Shibler if ((tp->t_state & TS_ISOPEN) == 0) { 49242354Smckusick #ifdef KGDB 49342354Smckusick if (unit == UNIT(kgdb_dev) && 49442354Smckusick (head = pp->r_head & RX_MASK) != (pp->r_tail & RX_MASK) && 49542354Smckusick dcm->dcm_rfifos[3-port][head>>1].data_char == '!') { 49642354Smckusick pp->r_head = (head + 2) & RX_MASK; 49742354Smckusick printf("kgdb trap from dcm%d\n", unit); 49842354Smckusick /* trap into kgdb */ 49942354Smckusick asm("trap #15;"); 50042354Smckusick return; 50142354Smckusick } 50242354Smckusick #endif 50342354Smckusick pp->r_head = pp->r_tail & RX_MASK; 50442354Smckusick return; 50542354Smckusick } 50641480Smckusick 50742354Smckusick head = pp->r_head & RX_MASK; 50842354Smckusick fifo = &dcm->dcm_rfifos[3-port][head>>1]; 50942354Smckusick /* 51042354Smckusick * XXX upper bound on how many chars we will take in one swallow? 51142354Smckusick */ 51242354Smckusick while (head != (pp->r_tail & RX_MASK)) { 51342354Smckusick /* 51442354Smckusick * Get character/status and update head pointer as fast 51542354Smckusick * as possible to make room for more characters. 51642354Smckusick */ 51742354Smckusick c = fifo->data_char; 51842354Smckusick stat = fifo->data_stat; 51941480Smckusick head = (head + 2) & RX_MASK; 52042354Smckusick pp->r_head = head; 52142354Smckusick fifo = head ? fifo+1 : &dcm->dcm_rfifos[3-port][0]; 52242354Smckusick nch++; 52341480Smckusick 52441480Smckusick #ifdef DEBUG 52541480Smckusick if (dcmdebug & DDB_INPUT) 52642354Smckusick printf("dcmreadbuf(%d): c%x('%c') s%x f%x h%x t%x\n", 52742354Smckusick unit, c&0xFF, c, stat&0xFF, 52842354Smckusick tp->t_flags, head, pp->r_tail); 52941480Smckusick #endif 53042354Smckusick /* 53142354Smckusick * Check for and handle errors 53242354Smckusick */ 53342354Smckusick if (stat & RD_MASK) { 53441480Smckusick #ifdef DEBUG 53542354Smckusick if (dcmdebug & (DDB_INPUT|DDB_SIOERR)) 53642354Smckusick printf("dcmreadbuf(%d): err: c%x('%c') s%x\n", 53742354Smckusick unit, stat, c&0xFF, c); 53841480Smckusick #endif 53941480Smckusick if (stat & (RD_BD | RD_FE)) 54041480Smckusick c |= TTY_FE; 54141480Smckusick else if (stat & RD_PE) 54241480Smckusick c |= TTY_PE; 54341480Smckusick else if (stat & RD_OVF) 54441480Smckusick log(LOG_WARNING, 54542354Smckusick "dcm%d: silo overflow\n", unit); 54641480Smckusick else if (stat & RD_OE) 54741480Smckusick log(LOG_WARNING, 54842354Smckusick "dcm%d: uart overflow\n", unit); 54941480Smckusick } 55041480Smckusick (*linesw[tp->t_line].l_rint)(c, tp); 55141480Smckusick } 55242354Smckusick dcmischeme[BOARD(unit)].dis_char += nch; 55342354Smckusick #ifdef IOSTATS 55442354Smckusick dsp->rchars += nch; 55542354Smckusick if (nch <= DCMRBSIZE) 55642354Smckusick dsp->rsilo[nch]++; 55741480Smckusick else 55842354Smckusick dsp->rsilo[DCMRBSIZE+1]++; 55941480Smckusick #endif 56041480Smckusick } 56141480Smckusick 56242354Smckusick dcmxint(unit, dcm, tp) 56341480Smckusick int unit; 56441480Smckusick struct dcmdevice *dcm; 56542354Smckusick register struct tty *tp; 56641480Smckusick { 56741480Smckusick tp->t_state &= ~TS_BUSY; 56841480Smckusick if (tp->t_state & TS_FLUSH) 56941480Smckusick tp->t_state &= ~TS_FLUSH; 57041480Smckusick if (tp->t_line) 57141480Smckusick (*linesw[tp->t_line].l_start)(tp); 57241480Smckusick else 57341480Smckusick dcmstart(tp); 57441480Smckusick } 57541480Smckusick 57641480Smckusick dcmmint(unit, mcnd, dcm) 57741480Smckusick register int unit; 57841480Smckusick register struct dcmdevice *dcm; 57941480Smckusick int mcnd; 58041480Smckusick { 58141480Smckusick register struct tty *tp; 58241480Smckusick 58341480Smckusick #ifdef DEBUG 58442354Smckusick if (dcmdebug & DDB_MODEM) 58541480Smckusick printf("dcmmint: unit %x mcnd %x\n", unit, mcnd); 58642354Smckusick #endif 58741480Smckusick tp = &dcm_tty[unit]; 58841480Smckusick if ((dcmsoftCAR[BOARD(unit)] & (1 << PORT(unit))) == 0) { 58941480Smckusick if (mcnd & MI_CD) 59041480Smckusick (void) (*linesw[tp->t_line].l_modem)(tp, 1); 59141480Smckusick else if ((*linesw[tp->t_line].l_modem)(tp, 0) == 0) { 59241480Smckusick dcm->dcm_mdmout &= ~(MO_DTR | MO_RTS); 59341480Smckusick SEM_LOCK(dcm); 59441480Smckusick dcm->dcm_cr |= CR_MODM; 59541480Smckusick SEM_UNLOCK(dcm); 59642354Smckusick DELAY(10); /* time to change lines */ 59741480Smckusick } 59841480Smckusick } 59941480Smckusick } 60041480Smckusick 60141480Smckusick dcmioctl(dev, cmd, data, flag) 60241480Smckusick dev_t dev; 60341480Smckusick caddr_t data; 60441480Smckusick { 60541480Smckusick register struct tty *tp; 60641480Smckusick register int unit = UNIT(dev); 60741480Smckusick register struct dcmdevice *dcm; 60841480Smckusick register int port; 60942354Smckusick int error, s; 61041480Smckusick 61141480Smckusick #ifdef DEBUG 61241480Smckusick if (dcmdebug & DDB_IOCTL) 61341480Smckusick printf("dcmioctl: unit %d cmd %x data %x flag %x\n", 61441480Smckusick unit, cmd, *data, flag); 61541480Smckusick #endif 61641480Smckusick tp = &dcm_tty[unit]; 61741480Smckusick error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag); 61841480Smckusick if (error >= 0) 61941480Smckusick return (error); 62041480Smckusick error = ttioctl(tp, cmd, data, flag); 62141480Smckusick if (error >= 0) 62241480Smckusick return (error); 62341480Smckusick 62441480Smckusick port = PORT(unit); 62541480Smckusick dcm = dcm_addr[BOARD(unit)]; 62641480Smckusick switch (cmd) { 62741480Smckusick case TIOCSBRK: 62842354Smckusick /* 62942354Smckusick * Wait for transmitter buffer to empty 63042354Smckusick */ 63142354Smckusick s = spltty(); 63242354Smckusick while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr) 63342354Smckusick DELAY(DCM_USPERCH(tp->t_ospeed)); 63441480Smckusick SEM_LOCK(dcm); 63542354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_BRK; 63642354Smckusick dcm->dcm_cr |= (1 << port); /* start break */ 63741480Smckusick SEM_UNLOCK(dcm); 63842354Smckusick splx(s); 63941480Smckusick break; 64041480Smckusick 64141480Smckusick case TIOCCBRK: 64241480Smckusick SEM_LOCK(dcm); 64342354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_BRK; 64442354Smckusick dcm->dcm_cr |= (1 << port); /* end break */ 64541480Smckusick SEM_UNLOCK(dcm); 64641480Smckusick break; 64741480Smckusick 64841480Smckusick case TIOCSDTR: 64941480Smckusick (void) dcmmctl(dev, MO_ON, DMBIS); 65041480Smckusick break; 65141480Smckusick 65241480Smckusick case TIOCCDTR: 65341480Smckusick (void) dcmmctl(dev, MO_ON, DMBIC); 65441480Smckusick break; 65541480Smckusick 65641480Smckusick case TIOCMSET: 65741480Smckusick (void) dcmmctl(dev, *(int *)data, DMSET); 65841480Smckusick break; 65941480Smckusick 66041480Smckusick case TIOCMBIS: 66141480Smckusick (void) dcmmctl(dev, *(int *)data, DMBIS); 66241480Smckusick break; 66341480Smckusick 66441480Smckusick case TIOCMBIC: 66541480Smckusick (void) dcmmctl(dev, *(int *)data, DMBIC); 66641480Smckusick break; 66741480Smckusick 66841480Smckusick case TIOCMGET: 66941480Smckusick *(int *)data = dcmmctl(dev, 0, DMGET); 67041480Smckusick break; 67141480Smckusick 67241480Smckusick default: 67341480Smckusick return (ENOTTY); 67441480Smckusick } 67541480Smckusick return (0); 67641480Smckusick } 67741480Smckusick 67841480Smckusick dcmparam(tp, t) 67941480Smckusick register struct tty *tp; 68041480Smckusick register struct termios *t; 68141480Smckusick { 68241480Smckusick register struct dcmdevice *dcm; 68342354Smckusick register int port, mode, cflag = t->c_cflag; 68441480Smckusick int ospeed = ttspeedtab(t->c_ospeed, dcmspeedtab); 68542354Smckusick 68641480Smckusick /* check requested parameters */ 68741480Smckusick if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)) 68841480Smckusick return(EINVAL); 68941480Smckusick /* and copy to tty */ 69041480Smckusick tp->t_ispeed = t->c_ispeed; 69141480Smckusick tp->t_ospeed = t->c_ospeed; 69241480Smckusick tp->t_cflag = cflag; 69341480Smckusick if (ospeed == 0) { 69442354Smckusick (void) dcmmctl(UNIT(tp->t_dev), MO_OFF, DMSET); 69542354Smckusick return(0); 69641480Smckusick } 69742354Smckusick 69842354Smckusick mode = 0; 69941480Smckusick switch (cflag&CSIZE) { 70041480Smckusick case CS5: 70141480Smckusick mode = LC_5BITS; break; 70241480Smckusick case CS6: 70341480Smckusick mode = LC_6BITS; break; 70441480Smckusick case CS7: 70541480Smckusick mode = LC_7BITS; break; 70641480Smckusick case CS8: 70741480Smckusick mode = LC_8BITS; break; 70841480Smckusick } 70941480Smckusick if (cflag&PARENB) { 71041480Smckusick if (cflag&PARODD) 71141480Smckusick mode |= LC_PODD; 71241480Smckusick else 71341480Smckusick mode |= LC_PEVEN; 71441480Smckusick } 71541480Smckusick if (cflag&CSTOPB) 71641480Smckusick mode |= LC_2STOP; 71741480Smckusick else 71841480Smckusick mode |= LC_1STOP; 71941480Smckusick #ifdef DEBUG 72041480Smckusick if (dcmdebug & DDB_PARAM) 72142354Smckusick printf("dcmparam(%d): cflag %x mode %x speed %d uperch %d\n", 72242354Smckusick UNIT(tp->t_dev), cflag, mode, tp->t_ospeed, 72342354Smckusick DCM_USPERCH(tp->t_ospeed)); 72441480Smckusick #endif 72542354Smckusick 72642354Smckusick port = PORT(tp->t_dev); 72742354Smckusick dcm = dcm_addr[BOARD(tp->t_dev)]; 72842354Smckusick /* 72942354Smckusick * Wait for transmitter buffer to empty. 73042354Smckusick */ 73141480Smckusick while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr) 73242354Smckusick DELAY(DCM_USPERCH(tp->t_ospeed)); 73342354Smckusick /* 73442354Smckusick * Make changes known to hardware. 73542354Smckusick */ 73642354Smckusick dcm->dcm_data[port].dcm_baud = ospeed; 73741480Smckusick dcm->dcm_data[port].dcm_conf = mode; 73841480Smckusick SEM_LOCK(dcm); 73942354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_CON; 74042354Smckusick dcm->dcm_cr |= (1 << port); 74141480Smckusick SEM_UNLOCK(dcm); 74242354Smckusick /* 74342354Smckusick * Delay for config change to take place. Weighted by buad. 74442354Smckusick * XXX why do we do this? 74542354Smckusick */ 74642354Smckusick DELAY(16 * DCM_USPERCH(tp->t_ospeed)); 74742354Smckusick return(0); 74841480Smckusick } 74941480Smckusick 75041480Smckusick dcmstart(tp) 75141480Smckusick register struct tty *tp; 75241480Smckusick { 75341480Smckusick register struct dcmdevice *dcm; 75442354Smckusick register struct dcmpreg *pp; 75542354Smckusick register struct dcmtfifo *fifo; 75642354Smckusick register char *bp; 75742354Smckusick register unsigned tail, next; 75842354Smckusick register int port, nch; 75942354Smckusick unsigned head; 76042354Smckusick char buf[16]; 76142354Smckusick int s; 76242354Smckusick #ifdef IOSTATS 76342354Smckusick struct dcmstats *dsp = &dcmstats[BOARD(tp->t_dev)]; 76442354Smckusick int tch = 0; 76542354Smckusick #endif 76642354Smckusick 76741480Smckusick s = spltty(); 76842354Smckusick #ifdef IOSTATS 76942354Smckusick dsp->xints++; 77042354Smckusick #endif 77141480Smckusick #ifdef DEBUG 77241480Smckusick if (dcmdebug & DDB_OUTPUT) 77342354Smckusick printf("dcmstart(%d): state %x flags %x outcc %d\n", 77442354Smckusick UNIT(tp->t_dev), tp->t_state, tp->t_flags, 77542354Smckusick tp->t_outq.c_cc); 77641480Smckusick #endif 77741480Smckusick if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) 77841480Smckusick goto out; 77941480Smckusick if (tp->t_outq.c_cc <= tp->t_lowat) { 78041480Smckusick if (tp->t_state&TS_ASLEEP) { 78141480Smckusick tp->t_state &= ~TS_ASLEEP; 78241480Smckusick wakeup((caddr_t)&tp->t_outq); 78341480Smckusick } 78441480Smckusick if (tp->t_wsel) { 78541480Smckusick selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL); 78641480Smckusick tp->t_wsel = 0; 78741480Smckusick tp->t_state &= ~TS_WCOLL; 78841480Smckusick } 78941480Smckusick } 79042354Smckusick if (tp->t_outq.c_cc == 0) { 79142354Smckusick #ifdef IOSTATS 79242354Smckusick dsp->xempty++; 79342354Smckusick #endif 79442354Smckusick goto out; 79542354Smckusick } 79642354Smckusick 79742354Smckusick dcm = dcm_addr[BOARD(tp->t_dev)]; 79842354Smckusick port = PORT(tp->t_dev); 79942354Smckusick pp = dcm_preg(dcm, port); 80042354Smckusick tail = pp->t_tail & TX_MASK; 80142354Smckusick next = (tail + 1) & TX_MASK; 80242354Smckusick head = pp->t_head & TX_MASK; 80342354Smckusick if (head == next) 80442354Smckusick goto out; 80542354Smckusick fifo = &dcm->dcm_tfifos[3-port][tail]; 80642354Smckusick again: 80742354Smckusick nch = q_to_b(&tp->t_outq, buf, (head - next) & TX_MASK); 80842354Smckusick #ifdef IOSTATS 80942354Smckusick tch += nch; 81042354Smckusick #endif 81141480Smckusick #ifdef DEBUG 81242354Smckusick if (dcmdebug & DDB_OUTPUT) 81342354Smckusick printf("\thead %x tail %x nch %d\n", head, tail, nch); 81441480Smckusick #endif 81542354Smckusick /* 81642354Smckusick * Loop transmitting all the characters we can. 81742354Smckusick */ 81842354Smckusick for (bp = buf; --nch >= 0; bp++) { 81942354Smckusick fifo->data_char = *bp; 82042354Smckusick pp->t_tail = next; 82142354Smckusick /* 82242354Smckusick * If this is the first character, 82342354Smckusick * get the hardware moving right now. 82442354Smckusick */ 82542354Smckusick if (bp == buf) { 82642354Smckusick tp->t_state |= TS_BUSY; 82742354Smckusick SEM_LOCK(dcm); 82842354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_TX; 82942354Smckusick dcm->dcm_cr |= (1 << port); 83042354Smckusick SEM_UNLOCK(dcm); 83142354Smckusick } 83241480Smckusick tail = next; 83342354Smckusick fifo = tail ? fifo+1 : &dcm->dcm_tfifos[3-port][0]; 83442354Smckusick next = (next + 1) & TX_MASK; 83541480Smckusick } 83642354Smckusick /* 83742354Smckusick * Head changed while we were loading the buffer, 83842354Smckusick * go back and load some more if we can. 83942354Smckusick */ 84042354Smckusick if (tp->t_outq.c_cc && head != (pp->t_head & TX_MASK)) { 84142354Smckusick #ifdef IOSTATS 84242354Smckusick dsp->xrestarts++; 84342354Smckusick #endif 84442354Smckusick head = pp->t_head & TX_MASK; 84542354Smckusick goto again; 84642354Smckusick } 84742354Smckusick /* 84842354Smckusick * Kick it one last time in case it finished while we were 84942354Smckusick * loading the last time. 85042354Smckusick */ 85142354Smckusick if (bp > &buf[1]) { 85241480Smckusick tp->t_state |= TS_BUSY; 85341480Smckusick SEM_LOCK(dcm); 85442354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_TX; 85542354Smckusick dcm->dcm_cr |= (1 << port); 85642354Smckusick SEM_UNLOCK(dcm); 85742354Smckusick } 85841480Smckusick #ifdef DEBUG 85941480Smckusick if (dcmdebug & DDB_INTR) 860*43407Shibler printf("dcmstart(%d): head %x tail %x outqcc %d\n", 861*43407Shibler UNIT(tp->t_dev), head, tail, tp->t_outq.c_cc); 86241480Smckusick #endif 86341480Smckusick out: 86442354Smckusick #ifdef IOSTATS 86542354Smckusick dsp->xchars += tch; 86642354Smckusick if (tch <= DCMXBSIZE) 86742354Smckusick dsp->xsilo[tch]++; 86842354Smckusick else 86942354Smckusick dsp->xsilo[DCMXBSIZE+1]++; 87042354Smckusick #endif 87141480Smckusick splx(s); 87241480Smckusick } 87341480Smckusick 87441480Smckusick /* 87541480Smckusick * Stop output on a line. 87641480Smckusick */ 87741480Smckusick dcmstop(tp, flag) 87841480Smckusick register struct tty *tp; 87941480Smckusick { 88041480Smckusick int s; 88141480Smckusick 88241480Smckusick s = spltty(); 88341480Smckusick if (tp->t_state & TS_BUSY) { 88442354Smckusick /* XXX is there some way to safely stop transmission? */ 88541480Smckusick if ((tp->t_state&TS_TTSTOP)==0) 88641480Smckusick tp->t_state |= TS_FLUSH; 88741480Smckusick } 88841480Smckusick splx(s); 88941480Smckusick } 89041480Smckusick 89141480Smckusick /* Modem control */ 89241480Smckusick 89341480Smckusick dcmmctl(dev, bits, how) 89441480Smckusick dev_t dev; 89541480Smckusick int bits, how; 89641480Smckusick { 89741480Smckusick register struct dcmdevice *dcm; 89841480Smckusick int s, hit = 0; 89941480Smckusick 90042354Smckusick /* 90142354Smckusick * Only port 0 has modem control lines. 90242354Smckusick * XXX ok for now but needs to changed for the 8 port board. 90342354Smckusick */ 90441480Smckusick if (PORT(UNIT(dev)) != 0) 90541480Smckusick return(bits); 90641480Smckusick 90741480Smckusick dcm = dcm_addr[BOARD(UNIT(dev))]; 90841480Smckusick s = spltty(); 90941480Smckusick switch (how) { 91041480Smckusick 91141480Smckusick case DMSET: 91241480Smckusick dcm->dcm_mdmout = bits; 91341480Smckusick hit++; 91441480Smckusick break; 91541480Smckusick 91641480Smckusick case DMBIS: 91741480Smckusick dcm->dcm_mdmout |= bits; 91841480Smckusick hit++; 91941480Smckusick break; 92041480Smckusick 92141480Smckusick case DMBIC: 92241480Smckusick dcm->dcm_mdmout &= ~bits; 92341480Smckusick hit++; 92441480Smckusick break; 92541480Smckusick 92641480Smckusick case DMGET: 92741480Smckusick bits = dcm->dcm_mdmin; 92841480Smckusick break; 92941480Smckusick } 93041480Smckusick if (hit) { 93141480Smckusick SEM_LOCK(dcm); 93241480Smckusick dcm->dcm_cr |= CR_MODM; 93341480Smckusick SEM_UNLOCK(dcm); 93442354Smckusick DELAY(10); /* delay until done */ 93541480Smckusick (void) splx(s); 93641480Smckusick } 93741480Smckusick return(bits); 93841480Smckusick } 93941480Smckusick 94042354Smckusick /* 94142354Smckusick * Set board to either interrupt per-character or at a fixed interval. 94242354Smckusick */ 94342354Smckusick dcmsetischeme(brd, flags) 94442354Smckusick int brd, flags; 94541480Smckusick { 94642354Smckusick register struct dcmdevice *dcm = dcm_addr[brd]; 94742354Smckusick register struct dcmischeme *dis = &dcmischeme[brd]; 94841480Smckusick register int i; 94942354Smckusick u_char mask; 95042354Smckusick int perchar = flags & DIS_PERCHAR; 95141480Smckusick 95241480Smckusick #ifdef DEBUG 95342354Smckusick if (dcmdebug & DDB_INTSCHM) 95442354Smckusick printf("dcmsetischeme(%d, %d): cur %d, ints %d, chars %d\n", 95542354Smckusick brd, perchar, dis->dis_perchar, 95642354Smckusick dis->dis_intr, dis->dis_char); 95742354Smckusick if ((flags & DIS_RESET) == 0 && perchar == dis->dis_perchar) { 95842354Smckusick printf("dcmsetischeme(%d): redundent request %d\n", 95942354Smckusick brd, perchar); 96042354Smckusick return; 96142354Smckusick } 96242354Smckusick #endif 96342354Smckusick /* 96442354Smckusick * If perchar is non-zero, we enable interrupts on all characters 96542354Smckusick * otherwise we disable perchar interrupts and use periodic 96642354Smckusick * polling interrupts. 96742354Smckusick */ 96842354Smckusick dis->dis_perchar = perchar; 96942354Smckusick mask = perchar ? 0xf : 0x0; 97042354Smckusick for (i = 0; i < 256; i++) 97142354Smckusick dcm->dcm_bmap[i].data_data = mask; 97242354Smckusick /* 97342354Smckusick * Don't slow down tandem mode, interrupt on flow control 97442354Smckusick * chars for any port on the board. 97542354Smckusick */ 97642354Smckusick if (!perchar) { 97742354Smckusick register struct tty *tp = &dcm_tty[MKUNIT(brd, 0)]; 97842354Smckusick int c; 97942354Smckusick 98042354Smckusick for (i = 0; i < 4; i++, tp++) { 98142354Smckusick if ((c = tp->t_cc[VSTART]) != _POSIX_VDISABLE) 98242354Smckusick dcm->dcm_bmap[c].data_data |= (1 << i); 98342354Smckusick if ((c = tp->t_cc[VSTOP]) != _POSIX_VDISABLE) 98442354Smckusick dcm->dcm_bmap[c].data_data |= (1 << i); 98541480Smckusick } 98641480Smckusick } 98742354Smckusick /* 98842354Smckusick * Board starts with timer disabled so if first call is to 98942354Smckusick * set perchar mode then we don't want to toggle the timer. 99042354Smckusick */ 99142354Smckusick if (flags == (DIS_RESET|DIS_PERCHAR)) 99241480Smckusick return; 99342354Smckusick /* 99442354Smckusick * Toggle card 16.7ms interrupts (we first make sure that card 99542354Smckusick * has cleared the bit so it will see the toggle). 99642354Smckusick */ 99742354Smckusick while (dcm->dcm_cr & CR_TIMER) 99842354Smckusick ; 99941480Smckusick SEM_LOCK(dcm); 100042354Smckusick dcm->dcm_cr |= CR_TIMER; 100141480Smckusick SEM_UNLOCK(dcm); 100241480Smckusick } 100341480Smckusick 100441480Smckusick /* 100541480Smckusick * Following are all routines needed for DCM to act as console 100641480Smckusick */ 100742354Smckusick #include "machine/cons.h" 100841480Smckusick 100942354Smckusick dcmcnprobe(cp) 101042354Smckusick struct consdev *cp; 101141480Smckusick { 101242354Smckusick register struct hp_hw *hw; 101342354Smckusick int unit, i; 101442354Smckusick extern int dcmopen(); 101541480Smckusick 101642354Smckusick /* 101742354Smckusick * Implicitly assigns the lowest select code DCM card found to be 101842354Smckusick * logical unit 0 (actually CONUNIT). If your config file does 101942354Smckusick * anything different, you're screwed. 102042354Smckusick */ 102142354Smckusick for (hw = sc_table; hw->hw_type; hw++) 102242354Smckusick if (hw->hw_type == COMMDCM && !badaddr((short *)hw->hw_addr)) 102342354Smckusick break; 102442354Smckusick if (hw->hw_type != COMMDCM) { 102542354Smckusick cp->cn_pri = CN_DEAD; 102642354Smckusick return; 102742354Smckusick } 102842354Smckusick unit = CONUNIT; 102942354Smckusick dcm_addr[BOARD(CONUNIT)] = (struct dcmdevice *)hw->hw_addr; 103041480Smckusick 103142354Smckusick /* locate the major number */ 103242354Smckusick for (i = 0; i < nchrdev; i++) 103342354Smckusick if (cdevsw[i].d_open == dcmopen) 103442354Smckusick break; 103542354Smckusick 103642354Smckusick /* initialize required fields */ 103742354Smckusick cp->cn_dev = makedev(i, unit); 103842354Smckusick cp->cn_tp = &dcm_tty[unit]; 103942354Smckusick switch (dcm_addr[BOARD(unit)]->dcm_rsid) { 104042354Smckusick case DCMID: 104142354Smckusick cp->cn_pri = CN_NORMAL; 104242354Smckusick break; 104342354Smckusick case DCMID|DCMCON: 104442354Smckusick cp->cn_pri = CN_REMOTE; 104542354Smckusick break; 104642354Smckusick default: 104742354Smckusick cp->cn_pri = CN_DEAD; 104842354Smckusick break; 104942354Smckusick } 105042354Smckusick } 105142354Smckusick 105242354Smckusick dcmcninit(cp) 105342354Smckusick struct consdev *cp; 105442354Smckusick { 105542354Smckusick dcminit(cp->cn_dev, dcmdefaultrate); 105642354Smckusick dcmconsole = UNIT(cp->cn_dev); 105742354Smckusick } 105842354Smckusick 105942354Smckusick dcminit(dev, rate) 106042354Smckusick dev_t dev; 106142354Smckusick int rate; 106242354Smckusick { 106342354Smckusick register struct dcmdevice *dcm = dcm_addr[BOARD(dev)]; 106442354Smckusick int s, mode, port; 106542354Smckusick 106642354Smckusick port = PORT(dev); 106742354Smckusick mode = LC_8BITS | LC_1STOP; 106841480Smckusick s = splhigh(); 106942354Smckusick /* 107042354Smckusick * Wait for transmitter buffer to empty. 107142354Smckusick */ 107242354Smckusick while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr) 107342354Smckusick DELAY(DCM_USPERCH(rate)); 107442354Smckusick /* 107542354Smckusick * Make changes known to hardware. 107642354Smckusick */ 107742354Smckusick dcm->dcm_data[port].dcm_baud = ttspeedtab(rate, dcmspeedtab); 107842354Smckusick dcm->dcm_data[port].dcm_conf = mode; 107942354Smckusick SEM_LOCK(dcm); 108042354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_CON; 108142354Smckusick dcm->dcm_cr |= (1 << port); 108242354Smckusick SEM_UNLOCK(dcm); 108342354Smckusick /* 108442354Smckusick * Delay for config change to take place. Weighted by buad. 108542354Smckusick * XXX why do we do this? 108642354Smckusick */ 108742354Smckusick DELAY(16 * DCM_USPERCH(rate)); 108841480Smckusick splx(s); 108941480Smckusick } 109041480Smckusick 109141480Smckusick dcmcngetc(dev) 109242354Smckusick dev_t dev; 109341480Smckusick { 109442354Smckusick register struct dcmdevice *dcm = dcm_addr[BOARD(dev)]; 109542354Smckusick register struct dcmrfifo *fifo; 109642354Smckusick register struct dcmpreg *pp; 109742354Smckusick register unsigned head; 109842354Smckusick int s, c, stat, port; 109942354Smckusick 110042354Smckusick port = PORT(dev); 110142354Smckusick pp = dcm_preg(dcm, port); 110242354Smckusick s = splhigh(); 110342354Smckusick head = pp->r_head & RX_MASK; 110442354Smckusick fifo = &dcm->dcm_rfifos[3-port][head>>1]; 110542354Smckusick while (head == (pp->r_tail & RX_MASK)) 110642354Smckusick ; 110742354Smckusick /* 110842354Smckusick * If board interrupts are enabled, just let our received char 110942354Smckusick * interrupt through in case some other port on the board was 111042354Smckusick * busy. Otherwise we must clear the interrupt. 111142354Smckusick */ 111242354Smckusick SEM_LOCK(dcm); 111342354Smckusick if ((dcm->dcm_ic & IC_IE) == 0) 111442354Smckusick stat = dcm->dcm_iir; 111542354Smckusick SEM_UNLOCK(dcm); 111642354Smckusick c = fifo->data_char; 111742354Smckusick stat = fifo->data_stat; 111842354Smckusick pp->r_head = (head + 2) & RX_MASK; 111942354Smckusick splx(s); 112042354Smckusick return(c); 112141480Smckusick } 112241480Smckusick 112341480Smckusick /* 112441480Smckusick * Console kernel output character routine. 112541480Smckusick */ 112641480Smckusick dcmcnputc(dev, c) 112741480Smckusick dev_t dev; 112842354Smckusick int c; 112941480Smckusick { 113041480Smckusick register struct dcmdevice *dcm = dcm_addr[BOARD(dev)]; 113142354Smckusick register struct dcmpreg *pp; 113242354Smckusick unsigned tail; 113342354Smckusick int s, port, stat; 113441480Smckusick 113542354Smckusick port = PORT(dev); 113642354Smckusick pp = dcm_preg(dcm, port); 113742354Smckusick s = splhigh(); 113842354Smckusick #ifdef KGDB 113942354Smckusick if (dev != kgdb_dev) 114042354Smckusick #endif 114142354Smckusick if (dcmconsole == -1) { 114242354Smckusick (void) dcminit(dev, dcmdefaultrate); 114342354Smckusick dcmconsole = UNIT(dev); 114442354Smckusick } 114542354Smckusick tail = pp->t_tail & TX_MASK; 114642354Smckusick while (tail != (pp->t_head & TX_MASK)) 114742354Smckusick ; 114842354Smckusick dcm->dcm_tfifos[3-port][tail].data_char = c; 114942354Smckusick pp->t_tail = tail = (tail + 1) & TX_MASK; 115041480Smckusick SEM_LOCK(dcm); 115142354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_TX; 115242354Smckusick dcm->dcm_cr |= (1 << port); 115341480Smckusick SEM_UNLOCK(dcm); 115442354Smckusick while (tail != (pp->t_head & TX_MASK)) 115542354Smckusick ; 115642354Smckusick /* 115742354Smckusick * If board interrupts are enabled, just let our completion 115842354Smckusick * interrupt through in case some other port on the board 115942354Smckusick * was busy. Otherwise we must clear the interrupt. 116042354Smckusick */ 116142354Smckusick if ((dcm->dcm_ic & IC_IE) == 0) { 116242354Smckusick SEM_LOCK(dcm); 116342354Smckusick stat = dcm->dcm_iir; 116442354Smckusick SEM_UNLOCK(dcm); 116542354Smckusick } 116641480Smckusick splx(s); 116741480Smckusick } 116841480Smckusick #endif 1169