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*42946Smarc * @(#)dcm.c 7.3 (Berkeley) 06/06/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) { 26941480Smckusick ttychars(tp); 27041480Smckusick tp->t_iflag = TTYDEF_IFLAG; 27141480Smckusick tp->t_oflag = TTYDEF_OFLAG; 27241480Smckusick tp->t_cflag = TTYDEF_CFLAG; 27341480Smckusick tp->t_lflag = TTYDEF_LFLAG; 27441480Smckusick tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 27542354Smckusick (void) dcmparam(tp, &tp->t_termios); 27641480Smckusick ttsetwater(tp); 27741480Smckusick } else if (tp->t_state&TS_XCLUDE && u.u_uid != 0) 27841480Smckusick return (EBUSY); 27941480Smckusick if (PORT(unit) == 0) /* enable port 0 */ 28041480Smckusick (void) dcmmctl(dev, MO_ON, DMSET); 28141480Smckusick if (dcmsoftCAR[brd] & (1 << PORT(unit))) 28241480Smckusick tp->t_state |= TS_CARR_ON; 28341480Smckusick else if (PORT(unit)) /* Only port 0 has modem control */ 28441480Smckusick tp->t_state |= TS_CARR_ON; 28541480Smckusick else if (dcmmctl(dev, MO_OFF, DMGET) & MI_CD) 28641480Smckusick tp->t_state |= TS_CARR_ON; 28741480Smckusick (void) spltty(); 28841480Smckusick while (!(flag&O_NONBLOCK) && !(tp->t_cflag&CLOCAL) && 28941480Smckusick (tp->t_state & TS_CARR_ON) == 0) { 29041480Smckusick tp->t_state |= TS_WOPEN; 291*42946Smarc if ((error = tsleep((caddr_t)&tp->t_rawq, TTIPRI | PCATCH, 292*42946Smarc ttopen, 0)) || 293*42946Smarc (error = ttclosed(tp))) { 29442354Smckusick tp->t_state &= ~TS_WOPEN; 29542354Smckusick (void) spl0(); 29642354Smckusick return (error); 29742354Smckusick } 29841480Smckusick } 29941480Smckusick (void) spl0(); 30041480Smckusick 30141480Smckusick #ifdef DEBUG 30241480Smckusick if (dcmdebug & DDB_OPENCLOSE) 30341480Smckusick printf("dcmopen: u %x st %x fl %x\n", 30441480Smckusick unit, tp->t_state, tp->t_flags); 30541480Smckusick #endif 30641480Smckusick return ((*linesw[tp->t_line].l_open)(dev, tp)); 30741480Smckusick } 30841480Smckusick 30941480Smckusick /*ARGSUSED*/ 31041480Smckusick dcmclose(dev, flag) 31141480Smckusick dev_t dev; 31241480Smckusick { 31341480Smckusick register struct tty *tp; 31441480Smckusick int unit; 31541480Smckusick 31641480Smckusick unit = UNIT(dev); 31741480Smckusick tp = &dcm_tty[unit]; 31841480Smckusick (*linesw[tp->t_line].l_close)(tp); 31941480Smckusick if (tp->t_cflag&HUPCL || tp->t_state&TS_WOPEN || 32041480Smckusick (tp->t_state&TS_ISOPEN) == 0) 32141480Smckusick (void) dcmmctl(dev, MO_OFF, DMSET); 32241480Smckusick #ifdef DEBUG 32341480Smckusick if (dcmdebug & DDB_OPENCLOSE) 32441480Smckusick printf("dcmclose: u %x st %x fl %x\n", 32541480Smckusick unit, tp->t_state, tp->t_flags); 32641480Smckusick #endif 32741480Smckusick ttyclose(tp); 32841480Smckusick return(0); 32941480Smckusick } 33041480Smckusick 33141480Smckusick dcmread(dev, uio, flag) 33241480Smckusick dev_t dev; 33341480Smckusick struct uio *uio; 33441480Smckusick { 33541480Smckusick register struct tty *tp; 33641480Smckusick 33741480Smckusick tp = &dcm_tty[UNIT(dev)]; 33841480Smckusick return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); 33941480Smckusick } 34041480Smckusick 34141480Smckusick dcmwrite(dev, uio, flag) 34241480Smckusick dev_t dev; 34341480Smckusick struct uio *uio; 34441480Smckusick { 34541480Smckusick int unit = UNIT(dev); 34641480Smckusick register struct tty *tp; 34741480Smckusick 34841480Smckusick tp = &dcm_tty[unit]; 34942354Smckusick /* 35042354Smckusick * XXX we disallow virtual consoles if the physical console is 35142354Smckusick * a serial port. This is in case there is a display attached that 35242354Smckusick * is not the console. In that situation we don't need/want the X 35342354Smckusick * server taking over the console. 35442354Smckusick */ 35542354Smckusick if (constty && unit == dcmconsole) 35642354Smckusick constty = NULL; 35741480Smckusick return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); 35841480Smckusick } 35941480Smckusick 36041480Smckusick dcmintr(brd) 36141480Smckusick register int brd; 36241480Smckusick { 36342354Smckusick register struct dcmdevice *dcm = dcm_addr[brd]; 36442354Smckusick register struct dcmischeme *dis; 36541480Smckusick int i, code, pcnd[4], mcnd, delta; 36641480Smckusick 36742354Smckusick /* 36842354Smckusick * Do all guarded register accesses right off to minimize 36942354Smckusick * block out of hardware. 37042354Smckusick */ 37141480Smckusick SEM_LOCK(dcm); 37241480Smckusick if ((dcm->dcm_ic & IC_IR) == 0) { 37341480Smckusick SEM_UNLOCK(dcm); 37441480Smckusick return(0); 37541480Smckusick } 37641480Smckusick for (i = 0; i < 4; i++) { 37741480Smckusick pcnd[i] = dcm->dcm_icrtab[i].dcm_data; 37841480Smckusick dcm->dcm_icrtab[i].dcm_data = 0; 37941480Smckusick } 38041480Smckusick mcnd = dcm->dcm_mdmin; 38141480Smckusick code = dcm->dcm_iir & IIR_MASK; 38242354Smckusick dcm->dcm_iir = 0; /* XXX doc claims read clears interrupt?! */ 38341480Smckusick SEM_UNLOCK(dcm); 38441480Smckusick 38541480Smckusick #ifdef DEBUG 38641480Smckusick if (dcmdebug & DDB_INTR) 38742354Smckusick printf("dcmintr(%d): iir %x p0 %x p1 %x p2 %x p3 %x m %x\n", 38842354Smckusick brd, code, pcnd[0], pcnd[1], pcnd[2], pcnd[3], mcnd); 38941480Smckusick #endif 39042354Smckusick if (code & IIR_TIMEO) 39142354Smckusick dcmrint(brd, dcm); 39241480Smckusick if (code & IIR_PORT0) 39341480Smckusick dcmpint(MKUNIT(brd, 0), pcnd[0], dcm); 39441480Smckusick if (code & IIR_PORT1) 39541480Smckusick dcmpint(MKUNIT(brd, 1), pcnd[1], dcm); 39641480Smckusick if (code & IIR_PORT2) 39741480Smckusick dcmpint(MKUNIT(brd, 2), pcnd[2], dcm); 39841480Smckusick if (code & IIR_PORT3) 39941480Smckusick dcmpint(MKUNIT(brd, 3), pcnd[3], dcm); 40041480Smckusick if (code & IIR_MODM) 40141480Smckusick dcmmint(MKUNIT(brd, 0), mcnd, dcm); /* always port 0 */ 40241480Smckusick 40342354Smckusick dis = &dcmischeme[brd]; 40441480Smckusick /* 40542354Smckusick * Chalk up a receiver interrupt if the timer running or one of 40642354Smckusick * the ports reports a special character interrupt. 40741480Smckusick */ 40842354Smckusick if ((code & IIR_TIMEO) || 40942354Smckusick ((pcnd[0]|pcnd[1]|pcnd[2]|pcnd[3]) & IT_SPEC)) 41042354Smckusick dis->dis_intr++; 41142354Smckusick /* 41242354Smckusick * See if it is time to check/change the interrupt rate. 41342354Smckusick */ 41442354Smckusick if (dcmistype < 0 && 41542354Smckusick (delta = time.tv_sec - dis->dis_time) >= dcminterval) { 41641480Smckusick /* 41742354Smckusick * If currently per-character and averaged over 70 interrupts 41842354Smckusick * per-second (66 is threshold of 600 baud) in last interval, 41942354Smckusick * switch to timer mode. 42042354Smckusick * 42142354Smckusick * XXX decay counts ala load average to avoid spikes? 42241480Smckusick */ 42342354Smckusick if (dis->dis_perchar && dis->dis_intr > 70 * delta) 42442354Smckusick dcmsetischeme(brd, DIS_TIMER); 42542354Smckusick /* 42642354Smckusick * If currently using timer and had more interrupts than 42742354Smckusick * received characters in the last interval, switch back 42842354Smckusick * to per-character. Note that after changing to per-char 42942354Smckusick * we must process any characters already in the queue 43042354Smckusick * since they may have arrived before the bitmap was setup. 43142354Smckusick * 43242354Smckusick * XXX decay counts? 43342354Smckusick */ 43442354Smckusick else if (!dis->dis_perchar && dis->dis_intr > dis->dis_char) { 43542354Smckusick dcmsetischeme(brd, DIS_PERCHAR); 43641480Smckusick dcmrint(brd, dcm); 43741480Smckusick } 43842354Smckusick dis->dis_intr = dis->dis_char = 0; 43942354Smckusick dis->dis_time = time.tv_sec; 44042354Smckusick } 44141480Smckusick return(1); 44241480Smckusick } 44341480Smckusick 44441480Smckusick /* 44541480Smckusick * Port interrupt. Can be two things: 44641480Smckusick * First, it might be a special character (exception interrupt); 44741480Smckusick * Second, it may be a buffer empty (transmit interrupt); 44841480Smckusick */ 44941480Smckusick dcmpint(unit, code, dcm) 45041480Smckusick int unit, code; 45142354Smckusick struct dcmdevice *dcm; 45241480Smckusick { 45342354Smckusick struct tty *tp = &dcm_tty[unit]; 45441480Smckusick 45542354Smckusick if (code & IT_SPEC) 45642354Smckusick dcmreadbuf(unit, dcm, tp); 45741480Smckusick if (code & IT_TX) 45842354Smckusick dcmxint(unit, dcm, tp); 45941480Smckusick } 46041480Smckusick 46141480Smckusick dcmrint(brd, dcm) 46241480Smckusick int brd; 46341480Smckusick register struct dcmdevice *dcm; 46441480Smckusick { 46542354Smckusick register int i, unit; 46641480Smckusick register struct tty *tp; 46741480Smckusick 46841480Smckusick unit = MKUNIT(brd, 0); 46941480Smckusick tp = &dcm_tty[unit]; 47042354Smckusick for (i = 0; i < 4; i++, tp++, unit++) 47142354Smckusick dcmreadbuf(unit, dcm, tp); 47241480Smckusick } 47341480Smckusick 47442354Smckusick dcmreadbuf(unit, dcm, tp) 47541480Smckusick int unit; 47641480Smckusick register struct dcmdevice *dcm; 47741480Smckusick register struct tty *tp; 47841480Smckusick { 47942354Smckusick int port = PORT(unit); 48042354Smckusick register struct dcmpreg *pp = dcm_preg(dcm, port); 48142354Smckusick register struct dcmrfifo *fifo; 48241480Smckusick register int c, stat; 48341480Smckusick register unsigned head; 48442354Smckusick int nch = 0; 48542354Smckusick #ifdef IOSTATS 48642354Smckusick struct dcmstats *dsp = &dcmstats[BOARD(unit)]; 48741480Smckusick 48842354Smckusick dsp->rints++; 48942354Smckusick #endif 49042354Smckusick /* 49142354Smckusick * TS_WOPEN catches a race when switching to polling mode from dcmrint 49242354Smckusick */ 49342354Smckusick if ((tp->t_state & (TS_ISOPEN|TS_WOPEN)) == 0) { 49442354Smckusick #ifdef KGDB 49542354Smckusick if (unit == UNIT(kgdb_dev) && 49642354Smckusick (head = pp->r_head & RX_MASK) != (pp->r_tail & RX_MASK) && 49742354Smckusick dcm->dcm_rfifos[3-port][head>>1].data_char == '!') { 49842354Smckusick pp->r_head = (head + 2) & RX_MASK; 49942354Smckusick printf("kgdb trap from dcm%d\n", unit); 50042354Smckusick /* trap into kgdb */ 50142354Smckusick asm("trap #15;"); 50242354Smckusick return; 50342354Smckusick } 50442354Smckusick #endif 50542354Smckusick pp->r_head = pp->r_tail & RX_MASK; 50642354Smckusick return; 50742354Smckusick } 50841480Smckusick 50942354Smckusick head = pp->r_head & RX_MASK; 51042354Smckusick fifo = &dcm->dcm_rfifos[3-port][head>>1]; 51142354Smckusick /* 51242354Smckusick * XXX upper bound on how many chars we will take in one swallow? 51342354Smckusick */ 51442354Smckusick while (head != (pp->r_tail & RX_MASK)) { 51542354Smckusick /* 51642354Smckusick * Get character/status and update head pointer as fast 51742354Smckusick * as possible to make room for more characters. 51842354Smckusick */ 51942354Smckusick c = fifo->data_char; 52042354Smckusick stat = fifo->data_stat; 52141480Smckusick head = (head + 2) & RX_MASK; 52242354Smckusick pp->r_head = head; 52342354Smckusick fifo = head ? fifo+1 : &dcm->dcm_rfifos[3-port][0]; 52442354Smckusick nch++; 52541480Smckusick 52641480Smckusick #ifdef DEBUG 52741480Smckusick if (dcmdebug & DDB_INPUT) 52842354Smckusick printf("dcmreadbuf(%d): c%x('%c') s%x f%x h%x t%x\n", 52942354Smckusick unit, c&0xFF, c, stat&0xFF, 53042354Smckusick tp->t_flags, head, pp->r_tail); 53141480Smckusick #endif 53242354Smckusick /* 53342354Smckusick * Check for and handle errors 53442354Smckusick */ 53542354Smckusick if (stat & RD_MASK) { 53641480Smckusick #ifdef DEBUG 53742354Smckusick if (dcmdebug & (DDB_INPUT|DDB_SIOERR)) 53842354Smckusick printf("dcmreadbuf(%d): err: c%x('%c') s%x\n", 53942354Smckusick unit, stat, c&0xFF, c); 54041480Smckusick #endif 54141480Smckusick if (stat & (RD_BD | RD_FE)) 54241480Smckusick c |= TTY_FE; 54341480Smckusick else if (stat & RD_PE) 54441480Smckusick c |= TTY_PE; 54541480Smckusick else if (stat & RD_OVF) 54641480Smckusick log(LOG_WARNING, 54742354Smckusick "dcm%d: silo overflow\n", unit); 54841480Smckusick else if (stat & RD_OE) 54941480Smckusick log(LOG_WARNING, 55042354Smckusick "dcm%d: uart overflow\n", unit); 55141480Smckusick } 55241480Smckusick (*linesw[tp->t_line].l_rint)(c, tp); 55341480Smckusick } 55442354Smckusick dcmischeme[BOARD(unit)].dis_char += nch; 55542354Smckusick #ifdef IOSTATS 55642354Smckusick dsp->rchars += nch; 55742354Smckusick if (nch <= DCMRBSIZE) 55842354Smckusick dsp->rsilo[nch]++; 55941480Smckusick else 56042354Smckusick dsp->rsilo[DCMRBSIZE+1]++; 56141480Smckusick #endif 56241480Smckusick } 56341480Smckusick 56442354Smckusick dcmxint(unit, dcm, tp) 56541480Smckusick int unit; 56641480Smckusick struct dcmdevice *dcm; 56742354Smckusick register struct tty *tp; 56841480Smckusick { 56941480Smckusick tp->t_state &= ~TS_BUSY; 57041480Smckusick if (tp->t_state & TS_FLUSH) 57141480Smckusick tp->t_state &= ~TS_FLUSH; 57241480Smckusick if (tp->t_line) 57341480Smckusick (*linesw[tp->t_line].l_start)(tp); 57441480Smckusick else 57541480Smckusick dcmstart(tp); 57641480Smckusick } 57741480Smckusick 57841480Smckusick dcmmint(unit, mcnd, dcm) 57941480Smckusick register int unit; 58041480Smckusick register struct dcmdevice *dcm; 58141480Smckusick int mcnd; 58241480Smckusick { 58341480Smckusick register struct tty *tp; 58441480Smckusick 58541480Smckusick #ifdef DEBUG 58642354Smckusick if (dcmdebug & DDB_MODEM) 58741480Smckusick printf("dcmmint: unit %x mcnd %x\n", unit, mcnd); 58842354Smckusick #endif 58941480Smckusick tp = &dcm_tty[unit]; 59041480Smckusick if ((dcmsoftCAR[BOARD(unit)] & (1 << PORT(unit))) == 0) { 59141480Smckusick if (mcnd & MI_CD) 59241480Smckusick (void) (*linesw[tp->t_line].l_modem)(tp, 1); 59341480Smckusick else if ((*linesw[tp->t_line].l_modem)(tp, 0) == 0) { 59441480Smckusick dcm->dcm_mdmout &= ~(MO_DTR | MO_RTS); 59541480Smckusick SEM_LOCK(dcm); 59641480Smckusick dcm->dcm_cr |= CR_MODM; 59741480Smckusick SEM_UNLOCK(dcm); 59842354Smckusick DELAY(10); /* time to change lines */ 59941480Smckusick } 60041480Smckusick } 60141480Smckusick } 60241480Smckusick 60341480Smckusick dcmioctl(dev, cmd, data, flag) 60441480Smckusick dev_t dev; 60541480Smckusick caddr_t data; 60641480Smckusick { 60741480Smckusick register struct tty *tp; 60841480Smckusick register int unit = UNIT(dev); 60941480Smckusick register struct dcmdevice *dcm; 61041480Smckusick register int port; 61142354Smckusick int error, s; 61241480Smckusick 61341480Smckusick #ifdef DEBUG 61441480Smckusick if (dcmdebug & DDB_IOCTL) 61541480Smckusick printf("dcmioctl: unit %d cmd %x data %x flag %x\n", 61641480Smckusick unit, cmd, *data, flag); 61741480Smckusick #endif 61841480Smckusick tp = &dcm_tty[unit]; 61941480Smckusick error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag); 62041480Smckusick if (error >= 0) 62141480Smckusick return (error); 62241480Smckusick error = ttioctl(tp, cmd, data, flag); 62341480Smckusick if (error >= 0) 62441480Smckusick return (error); 62541480Smckusick 62641480Smckusick port = PORT(unit); 62741480Smckusick dcm = dcm_addr[BOARD(unit)]; 62841480Smckusick switch (cmd) { 62941480Smckusick case TIOCSBRK: 63042354Smckusick /* 63142354Smckusick * Wait for transmitter buffer to empty 63242354Smckusick */ 63342354Smckusick s = spltty(); 63442354Smckusick while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr) 63542354Smckusick DELAY(DCM_USPERCH(tp->t_ospeed)); 63641480Smckusick SEM_LOCK(dcm); 63742354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_BRK; 63842354Smckusick dcm->dcm_cr |= (1 << port); /* start break */ 63941480Smckusick SEM_UNLOCK(dcm); 64042354Smckusick splx(s); 64141480Smckusick break; 64241480Smckusick 64341480Smckusick case TIOCCBRK: 64441480Smckusick SEM_LOCK(dcm); 64542354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_BRK; 64642354Smckusick dcm->dcm_cr |= (1 << port); /* end break */ 64741480Smckusick SEM_UNLOCK(dcm); 64841480Smckusick break; 64941480Smckusick 65041480Smckusick case TIOCSDTR: 65141480Smckusick (void) dcmmctl(dev, MO_ON, DMBIS); 65241480Smckusick break; 65341480Smckusick 65441480Smckusick case TIOCCDTR: 65541480Smckusick (void) dcmmctl(dev, MO_ON, DMBIC); 65641480Smckusick break; 65741480Smckusick 65841480Smckusick case TIOCMSET: 65941480Smckusick (void) dcmmctl(dev, *(int *)data, DMSET); 66041480Smckusick break; 66141480Smckusick 66241480Smckusick case TIOCMBIS: 66341480Smckusick (void) dcmmctl(dev, *(int *)data, DMBIS); 66441480Smckusick break; 66541480Smckusick 66641480Smckusick case TIOCMBIC: 66741480Smckusick (void) dcmmctl(dev, *(int *)data, DMBIC); 66841480Smckusick break; 66941480Smckusick 67041480Smckusick case TIOCMGET: 67141480Smckusick *(int *)data = dcmmctl(dev, 0, DMGET); 67241480Smckusick break; 67341480Smckusick 67441480Smckusick default: 67541480Smckusick return (ENOTTY); 67641480Smckusick } 67741480Smckusick return (0); 67841480Smckusick } 67941480Smckusick 68041480Smckusick dcmparam(tp, t) 68141480Smckusick register struct tty *tp; 68241480Smckusick register struct termios *t; 68341480Smckusick { 68441480Smckusick register struct dcmdevice *dcm; 68542354Smckusick register int port, mode, cflag = t->c_cflag; 68641480Smckusick int ospeed = ttspeedtab(t->c_ospeed, dcmspeedtab); 68742354Smckusick 68841480Smckusick /* check requested parameters */ 68941480Smckusick if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)) 69041480Smckusick return(EINVAL); 69141480Smckusick /* and copy to tty */ 69241480Smckusick tp->t_ispeed = t->c_ispeed; 69341480Smckusick tp->t_ospeed = t->c_ospeed; 69441480Smckusick tp->t_cflag = cflag; 69541480Smckusick if (ospeed == 0) { 69642354Smckusick (void) dcmmctl(UNIT(tp->t_dev), MO_OFF, DMSET); 69742354Smckusick return(0); 69841480Smckusick } 69942354Smckusick 70042354Smckusick mode = 0; 70141480Smckusick switch (cflag&CSIZE) { 70241480Smckusick case CS5: 70341480Smckusick mode = LC_5BITS; break; 70441480Smckusick case CS6: 70541480Smckusick mode = LC_6BITS; break; 70641480Smckusick case CS7: 70741480Smckusick mode = LC_7BITS; break; 70841480Smckusick case CS8: 70941480Smckusick mode = LC_8BITS; break; 71041480Smckusick } 71141480Smckusick if (cflag&PARENB) { 71241480Smckusick if (cflag&PARODD) 71341480Smckusick mode |= LC_PODD; 71441480Smckusick else 71541480Smckusick mode |= LC_PEVEN; 71641480Smckusick } 71741480Smckusick if (cflag&CSTOPB) 71841480Smckusick mode |= LC_2STOP; 71941480Smckusick else 72041480Smckusick mode |= LC_1STOP; 72141480Smckusick #ifdef DEBUG 72241480Smckusick if (dcmdebug & DDB_PARAM) 72342354Smckusick printf("dcmparam(%d): cflag %x mode %x speed %d uperch %d\n", 72442354Smckusick UNIT(tp->t_dev), cflag, mode, tp->t_ospeed, 72542354Smckusick DCM_USPERCH(tp->t_ospeed)); 72641480Smckusick #endif 72742354Smckusick 72842354Smckusick port = PORT(tp->t_dev); 72942354Smckusick dcm = dcm_addr[BOARD(tp->t_dev)]; 73042354Smckusick /* 73142354Smckusick * Wait for transmitter buffer to empty. 73242354Smckusick */ 73341480Smckusick while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr) 73442354Smckusick DELAY(DCM_USPERCH(tp->t_ospeed)); 73542354Smckusick /* 73642354Smckusick * Make changes known to hardware. 73742354Smckusick */ 73842354Smckusick dcm->dcm_data[port].dcm_baud = ospeed; 73941480Smckusick dcm->dcm_data[port].dcm_conf = mode; 74041480Smckusick SEM_LOCK(dcm); 74142354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_CON; 74242354Smckusick dcm->dcm_cr |= (1 << port); 74341480Smckusick SEM_UNLOCK(dcm); 74442354Smckusick /* 74542354Smckusick * Delay for config change to take place. Weighted by buad. 74642354Smckusick * XXX why do we do this? 74742354Smckusick */ 74842354Smckusick DELAY(16 * DCM_USPERCH(tp->t_ospeed)); 74942354Smckusick return(0); 75041480Smckusick } 75141480Smckusick 75241480Smckusick dcmstart(tp) 75341480Smckusick register struct tty *tp; 75441480Smckusick { 75541480Smckusick register struct dcmdevice *dcm; 75642354Smckusick register struct dcmpreg *pp; 75742354Smckusick register struct dcmtfifo *fifo; 75842354Smckusick register char *bp; 75942354Smckusick register unsigned tail, next; 76042354Smckusick register int port, nch; 76142354Smckusick unsigned head; 76242354Smckusick char buf[16]; 76342354Smckusick int s; 76442354Smckusick #ifdef IOSTATS 76542354Smckusick struct dcmstats *dsp = &dcmstats[BOARD(tp->t_dev)]; 76642354Smckusick int tch = 0; 76742354Smckusick #endif 76842354Smckusick 76941480Smckusick s = spltty(); 77042354Smckusick #ifdef IOSTATS 77142354Smckusick dsp->xints++; 77242354Smckusick #endif 77341480Smckusick #ifdef DEBUG 77441480Smckusick if (dcmdebug & DDB_OUTPUT) 77542354Smckusick printf("dcmstart(%d): state %x flags %x outcc %d\n", 77642354Smckusick UNIT(tp->t_dev), tp->t_state, tp->t_flags, 77742354Smckusick tp->t_outq.c_cc); 77841480Smckusick #endif 77941480Smckusick if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) 78041480Smckusick goto out; 78141480Smckusick if (tp->t_outq.c_cc <= tp->t_lowat) { 78241480Smckusick if (tp->t_state&TS_ASLEEP) { 78341480Smckusick tp->t_state &= ~TS_ASLEEP; 78441480Smckusick wakeup((caddr_t)&tp->t_outq); 78541480Smckusick } 78641480Smckusick if (tp->t_wsel) { 78741480Smckusick selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL); 78841480Smckusick tp->t_wsel = 0; 78941480Smckusick tp->t_state &= ~TS_WCOLL; 79041480Smckusick } 79141480Smckusick } 79242354Smckusick if (tp->t_outq.c_cc == 0) { 79342354Smckusick #ifdef IOSTATS 79442354Smckusick dsp->xempty++; 79542354Smckusick #endif 79642354Smckusick goto out; 79742354Smckusick } 79842354Smckusick 79942354Smckusick dcm = dcm_addr[BOARD(tp->t_dev)]; 80042354Smckusick port = PORT(tp->t_dev); 80142354Smckusick pp = dcm_preg(dcm, port); 80242354Smckusick tail = pp->t_tail & TX_MASK; 80342354Smckusick next = (tail + 1) & TX_MASK; 80442354Smckusick head = pp->t_head & TX_MASK; 80542354Smckusick if (head == next) 80642354Smckusick goto out; 80742354Smckusick fifo = &dcm->dcm_tfifos[3-port][tail]; 80842354Smckusick again: 80942354Smckusick nch = q_to_b(&tp->t_outq, buf, (head - next) & TX_MASK); 81042354Smckusick #ifdef IOSTATS 81142354Smckusick tch += nch; 81242354Smckusick #endif 81341480Smckusick #ifdef DEBUG 81442354Smckusick if (dcmdebug & DDB_OUTPUT) 81542354Smckusick printf("\thead %x tail %x nch %d\n", head, tail, nch); 81641480Smckusick #endif 81742354Smckusick /* 81842354Smckusick * Loop transmitting all the characters we can. 81942354Smckusick */ 82042354Smckusick for (bp = buf; --nch >= 0; bp++) { 82142354Smckusick fifo->data_char = *bp; 82242354Smckusick pp->t_tail = next; 82342354Smckusick /* 82442354Smckusick * If this is the first character, 82542354Smckusick * get the hardware moving right now. 82642354Smckusick */ 82742354Smckusick if (bp == buf) { 82842354Smckusick tp->t_state |= TS_BUSY; 82942354Smckusick SEM_LOCK(dcm); 83042354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_TX; 83142354Smckusick dcm->dcm_cr |= (1 << port); 83242354Smckusick SEM_UNLOCK(dcm); 83342354Smckusick } 83441480Smckusick tail = next; 83542354Smckusick fifo = tail ? fifo+1 : &dcm->dcm_tfifos[3-port][0]; 83642354Smckusick next = (next + 1) & TX_MASK; 83741480Smckusick } 83842354Smckusick /* 83942354Smckusick * Head changed while we were loading the buffer, 84042354Smckusick * go back and load some more if we can. 84142354Smckusick */ 84242354Smckusick if (tp->t_outq.c_cc && head != (pp->t_head & TX_MASK)) { 84342354Smckusick #ifdef IOSTATS 84442354Smckusick dsp->xrestarts++; 84542354Smckusick #endif 84642354Smckusick head = pp->t_head & TX_MASK; 84742354Smckusick goto again; 84842354Smckusick } 84942354Smckusick /* 85042354Smckusick * Kick it one last time in case it finished while we were 85142354Smckusick * loading the last time. 85242354Smckusick */ 85342354Smckusick if (bp > &buf[1]) { 85441480Smckusick tp->t_state |= TS_BUSY; 85541480Smckusick SEM_LOCK(dcm); 85642354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_TX; 85742354Smckusick dcm->dcm_cr |= (1 << port); 85842354Smckusick SEM_UNLOCK(dcm); 85942354Smckusick } 86041480Smckusick #ifdef DEBUG 86141480Smckusick if (dcmdebug & DDB_INTR) 86242354Smckusick printf("dcmstart(%d): head %x tail %x outqcc %d ch %d\n", 86342354Smckusick UNIT(tp->t_dev), head, tail, tp->t_outq.c_cc, tch); 86441480Smckusick #endif 86541480Smckusick out: 86642354Smckusick #ifdef IOSTATS 86742354Smckusick dsp->xchars += tch; 86842354Smckusick if (tch <= DCMXBSIZE) 86942354Smckusick dsp->xsilo[tch]++; 87042354Smckusick else 87142354Smckusick dsp->xsilo[DCMXBSIZE+1]++; 87242354Smckusick #endif 87341480Smckusick splx(s); 87441480Smckusick } 87541480Smckusick 87641480Smckusick /* 87741480Smckusick * Stop output on a line. 87841480Smckusick */ 87941480Smckusick dcmstop(tp, flag) 88041480Smckusick register struct tty *tp; 88141480Smckusick { 88241480Smckusick int s; 88341480Smckusick 88441480Smckusick s = spltty(); 88541480Smckusick if (tp->t_state & TS_BUSY) { 88642354Smckusick /* XXX is there some way to safely stop transmission? */ 88741480Smckusick if ((tp->t_state&TS_TTSTOP)==0) 88841480Smckusick tp->t_state |= TS_FLUSH; 88941480Smckusick } 89041480Smckusick splx(s); 89141480Smckusick } 89241480Smckusick 89341480Smckusick /* Modem control */ 89441480Smckusick 89541480Smckusick dcmmctl(dev, bits, how) 89641480Smckusick dev_t dev; 89741480Smckusick int bits, how; 89841480Smckusick { 89941480Smckusick register struct dcmdevice *dcm; 90041480Smckusick int s, hit = 0; 90141480Smckusick 90242354Smckusick /* 90342354Smckusick * Only port 0 has modem control lines. 90442354Smckusick * XXX ok for now but needs to changed for the 8 port board. 90542354Smckusick */ 90641480Smckusick if (PORT(UNIT(dev)) != 0) 90741480Smckusick return(bits); 90841480Smckusick 90941480Smckusick dcm = dcm_addr[BOARD(UNIT(dev))]; 91041480Smckusick s = spltty(); 91141480Smckusick switch (how) { 91241480Smckusick 91341480Smckusick case DMSET: 91441480Smckusick dcm->dcm_mdmout = bits; 91541480Smckusick hit++; 91641480Smckusick break; 91741480Smckusick 91841480Smckusick case DMBIS: 91941480Smckusick dcm->dcm_mdmout |= bits; 92041480Smckusick hit++; 92141480Smckusick break; 92241480Smckusick 92341480Smckusick case DMBIC: 92441480Smckusick dcm->dcm_mdmout &= ~bits; 92541480Smckusick hit++; 92641480Smckusick break; 92741480Smckusick 92841480Smckusick case DMGET: 92941480Smckusick bits = dcm->dcm_mdmin; 93041480Smckusick break; 93141480Smckusick } 93241480Smckusick if (hit) { 93341480Smckusick SEM_LOCK(dcm); 93441480Smckusick dcm->dcm_cr |= CR_MODM; 93541480Smckusick SEM_UNLOCK(dcm); 93642354Smckusick DELAY(10); /* delay until done */ 93741480Smckusick (void) splx(s); 93841480Smckusick } 93941480Smckusick return(bits); 94041480Smckusick } 94141480Smckusick 94242354Smckusick /* 94342354Smckusick * Set board to either interrupt per-character or at a fixed interval. 94442354Smckusick */ 94542354Smckusick dcmsetischeme(brd, flags) 94642354Smckusick int brd, flags; 94741480Smckusick { 94842354Smckusick register struct dcmdevice *dcm = dcm_addr[brd]; 94942354Smckusick register struct dcmischeme *dis = &dcmischeme[brd]; 95041480Smckusick register int i; 95142354Smckusick u_char mask; 95242354Smckusick int perchar = flags & DIS_PERCHAR; 95341480Smckusick 95441480Smckusick #ifdef DEBUG 95542354Smckusick if (dcmdebug & DDB_INTSCHM) 95642354Smckusick printf("dcmsetischeme(%d, %d): cur %d, ints %d, chars %d\n", 95742354Smckusick brd, perchar, dis->dis_perchar, 95842354Smckusick dis->dis_intr, dis->dis_char); 95942354Smckusick if ((flags & DIS_RESET) == 0 && perchar == dis->dis_perchar) { 96042354Smckusick printf("dcmsetischeme(%d): redundent request %d\n", 96142354Smckusick brd, perchar); 96242354Smckusick return; 96342354Smckusick } 96442354Smckusick #endif 96542354Smckusick /* 96642354Smckusick * If perchar is non-zero, we enable interrupts on all characters 96742354Smckusick * otherwise we disable perchar interrupts and use periodic 96842354Smckusick * polling interrupts. 96942354Smckusick */ 97042354Smckusick dis->dis_perchar = perchar; 97142354Smckusick mask = perchar ? 0xf : 0x0; 97242354Smckusick for (i = 0; i < 256; i++) 97342354Smckusick dcm->dcm_bmap[i].data_data = mask; 97442354Smckusick /* 97542354Smckusick * Don't slow down tandem mode, interrupt on flow control 97642354Smckusick * chars for any port on the board. 97742354Smckusick */ 97842354Smckusick if (!perchar) { 97942354Smckusick register struct tty *tp = &dcm_tty[MKUNIT(brd, 0)]; 98042354Smckusick int c; 98142354Smckusick 98242354Smckusick for (i = 0; i < 4; i++, tp++) { 98342354Smckusick if ((c = tp->t_cc[VSTART]) != _POSIX_VDISABLE) 98442354Smckusick dcm->dcm_bmap[c].data_data |= (1 << i); 98542354Smckusick if ((c = tp->t_cc[VSTOP]) != _POSIX_VDISABLE) 98642354Smckusick dcm->dcm_bmap[c].data_data |= (1 << i); 98741480Smckusick } 98841480Smckusick } 98942354Smckusick /* 99042354Smckusick * Board starts with timer disabled so if first call is to 99142354Smckusick * set perchar mode then we don't want to toggle the timer. 99242354Smckusick */ 99342354Smckusick if (flags == (DIS_RESET|DIS_PERCHAR)) 99441480Smckusick return; 99542354Smckusick /* 99642354Smckusick * Toggle card 16.7ms interrupts (we first make sure that card 99742354Smckusick * has cleared the bit so it will see the toggle). 99842354Smckusick */ 99942354Smckusick while (dcm->dcm_cr & CR_TIMER) 100042354Smckusick ; 100141480Smckusick SEM_LOCK(dcm); 100242354Smckusick dcm->dcm_cr |= CR_TIMER; 100341480Smckusick SEM_UNLOCK(dcm); 100441480Smckusick } 100541480Smckusick 100641480Smckusick /* 100741480Smckusick * Following are all routines needed for DCM to act as console 100841480Smckusick */ 100942354Smckusick #include "machine/cons.h" 101041480Smckusick 101142354Smckusick dcmcnprobe(cp) 101242354Smckusick struct consdev *cp; 101341480Smckusick { 101442354Smckusick register struct hp_hw *hw; 101542354Smckusick int unit, i; 101642354Smckusick extern int dcmopen(); 101741480Smckusick 101842354Smckusick /* 101942354Smckusick * Implicitly assigns the lowest select code DCM card found to be 102042354Smckusick * logical unit 0 (actually CONUNIT). If your config file does 102142354Smckusick * anything different, you're screwed. 102242354Smckusick */ 102342354Smckusick for (hw = sc_table; hw->hw_type; hw++) 102442354Smckusick if (hw->hw_type == COMMDCM && !badaddr((short *)hw->hw_addr)) 102542354Smckusick break; 102642354Smckusick if (hw->hw_type != COMMDCM) { 102742354Smckusick cp->cn_pri = CN_DEAD; 102842354Smckusick return; 102942354Smckusick } 103042354Smckusick unit = CONUNIT; 103142354Smckusick dcm_addr[BOARD(CONUNIT)] = (struct dcmdevice *)hw->hw_addr; 103241480Smckusick 103342354Smckusick /* locate the major number */ 103442354Smckusick for (i = 0; i < nchrdev; i++) 103542354Smckusick if (cdevsw[i].d_open == dcmopen) 103642354Smckusick break; 103742354Smckusick 103842354Smckusick /* initialize required fields */ 103942354Smckusick cp->cn_dev = makedev(i, unit); 104042354Smckusick cp->cn_tp = &dcm_tty[unit]; 104142354Smckusick switch (dcm_addr[BOARD(unit)]->dcm_rsid) { 104242354Smckusick case DCMID: 104342354Smckusick cp->cn_pri = CN_NORMAL; 104442354Smckusick break; 104542354Smckusick case DCMID|DCMCON: 104642354Smckusick cp->cn_pri = CN_REMOTE; 104742354Smckusick break; 104842354Smckusick default: 104942354Smckusick cp->cn_pri = CN_DEAD; 105042354Smckusick break; 105142354Smckusick } 105242354Smckusick } 105342354Smckusick 105442354Smckusick dcmcninit(cp) 105542354Smckusick struct consdev *cp; 105642354Smckusick { 105742354Smckusick dcminit(cp->cn_dev, dcmdefaultrate); 105842354Smckusick dcmconsole = UNIT(cp->cn_dev); 105942354Smckusick } 106042354Smckusick 106142354Smckusick dcminit(dev, rate) 106242354Smckusick dev_t dev; 106342354Smckusick int rate; 106442354Smckusick { 106542354Smckusick register struct dcmdevice *dcm = dcm_addr[BOARD(dev)]; 106642354Smckusick int s, mode, port; 106742354Smckusick 106842354Smckusick port = PORT(dev); 106942354Smckusick mode = LC_8BITS | LC_1STOP; 107041480Smckusick s = splhigh(); 107142354Smckusick /* 107242354Smckusick * Wait for transmitter buffer to empty. 107342354Smckusick */ 107442354Smckusick while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr) 107542354Smckusick DELAY(DCM_USPERCH(rate)); 107642354Smckusick /* 107742354Smckusick * Make changes known to hardware. 107842354Smckusick */ 107942354Smckusick dcm->dcm_data[port].dcm_baud = ttspeedtab(rate, dcmspeedtab); 108042354Smckusick dcm->dcm_data[port].dcm_conf = mode; 108142354Smckusick SEM_LOCK(dcm); 108242354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_CON; 108342354Smckusick dcm->dcm_cr |= (1 << port); 108442354Smckusick SEM_UNLOCK(dcm); 108542354Smckusick /* 108642354Smckusick * Delay for config change to take place. Weighted by buad. 108742354Smckusick * XXX why do we do this? 108842354Smckusick */ 108942354Smckusick DELAY(16 * DCM_USPERCH(rate)); 109041480Smckusick splx(s); 109141480Smckusick } 109241480Smckusick 109341480Smckusick dcmcngetc(dev) 109442354Smckusick dev_t dev; 109541480Smckusick { 109642354Smckusick register struct dcmdevice *dcm = dcm_addr[BOARD(dev)]; 109742354Smckusick register struct dcmrfifo *fifo; 109842354Smckusick register struct dcmpreg *pp; 109942354Smckusick register unsigned head; 110042354Smckusick int s, c, stat, port; 110142354Smckusick 110242354Smckusick port = PORT(dev); 110342354Smckusick pp = dcm_preg(dcm, port); 110442354Smckusick s = splhigh(); 110542354Smckusick head = pp->r_head & RX_MASK; 110642354Smckusick fifo = &dcm->dcm_rfifos[3-port][head>>1]; 110742354Smckusick while (head == (pp->r_tail & RX_MASK)) 110842354Smckusick ; 110942354Smckusick /* 111042354Smckusick * If board interrupts are enabled, just let our received char 111142354Smckusick * interrupt through in case some other port on the board was 111242354Smckusick * busy. Otherwise we must clear the interrupt. 111342354Smckusick */ 111442354Smckusick SEM_LOCK(dcm); 111542354Smckusick if ((dcm->dcm_ic & IC_IE) == 0) 111642354Smckusick stat = dcm->dcm_iir; 111742354Smckusick SEM_UNLOCK(dcm); 111842354Smckusick c = fifo->data_char; 111942354Smckusick stat = fifo->data_stat; 112042354Smckusick pp->r_head = (head + 2) & RX_MASK; 112142354Smckusick splx(s); 112242354Smckusick return(c); 112341480Smckusick } 112441480Smckusick 112541480Smckusick /* 112641480Smckusick * Console kernel output character routine. 112741480Smckusick */ 112841480Smckusick dcmcnputc(dev, c) 112941480Smckusick dev_t dev; 113042354Smckusick int c; 113141480Smckusick { 113241480Smckusick register struct dcmdevice *dcm = dcm_addr[BOARD(dev)]; 113342354Smckusick register struct dcmpreg *pp; 113442354Smckusick unsigned tail; 113542354Smckusick int s, port, stat; 113641480Smckusick 113742354Smckusick port = PORT(dev); 113842354Smckusick pp = dcm_preg(dcm, port); 113942354Smckusick s = splhigh(); 114042354Smckusick #ifdef KGDB 114142354Smckusick if (dev != kgdb_dev) 114242354Smckusick #endif 114342354Smckusick if (dcmconsole == -1) { 114442354Smckusick (void) dcminit(dev, dcmdefaultrate); 114542354Smckusick dcmconsole = UNIT(dev); 114642354Smckusick } 114742354Smckusick tail = pp->t_tail & TX_MASK; 114842354Smckusick while (tail != (pp->t_head & TX_MASK)) 114942354Smckusick ; 115042354Smckusick dcm->dcm_tfifos[3-port][tail].data_char = c; 115142354Smckusick pp->t_tail = tail = (tail + 1) & TX_MASK; 115241480Smckusick SEM_LOCK(dcm); 115342354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_TX; 115442354Smckusick dcm->dcm_cr |= (1 << port); 115541480Smckusick SEM_UNLOCK(dcm); 115642354Smckusick while (tail != (pp->t_head & TX_MASK)) 115742354Smckusick ; 115842354Smckusick /* 115942354Smckusick * If board interrupts are enabled, just let our completion 116042354Smckusick * interrupt through in case some other port on the board 116142354Smckusick * was busy. Otherwise we must clear the interrupt. 116242354Smckusick */ 116342354Smckusick if ((dcm->dcm_ic & IC_IE) == 0) { 116442354Smckusick SEM_LOCK(dcm); 116542354Smckusick stat = dcm->dcm_iir; 116642354Smckusick SEM_UNLOCK(dcm); 116742354Smckusick } 116841480Smckusick splx(s); 116941480Smckusick } 117041480Smckusick #endif 1171