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 * 1245750Smckusick * from: $Hdr: dcm.c 1.1 90/07/09$ 1341480Smckusick * 14*45788Sbostic * @(#)dcm.c 7.10 (Berkeley) 12/16/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 */ 28*45788Sbostic #include "sys/param.h" 29*45788Sbostic #include "sys/systm.h" 30*45788Sbostic #include "sys/ioctl.h" 31*45788Sbostic #include "sys/tty.h" 32*45788Sbostic #include "sys/user.h" 33*45788Sbostic #include "sys/conf.h" 34*45788Sbostic #include "sys/file.h" 35*45788Sbostic #include "sys/uio.h" 36*45788Sbostic #include "sys/kernel.h" 37*45788Sbostic #include "sys/syslog.h" 38*45788Sbostic #include "sys/time.h" 3941480Smckusick 4041480Smckusick #include "device.h" 4141480Smckusick #include "dcmreg.h" 42*45788Sbostic #include "../include/cpu.h" 43*45788Sbostic #include "../hp300/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]; 5945750Smckusick struct modemreg *dcm_modem[NDCMLINE]; 6044317Shibler char mcndlast[NDCMLINE]; /* XXX last modem status for line */ 6142354Smckusick int ndcm = NDCMLINE; 6242354Smckusick 6342354Smckusick int dcm_active; 6441480Smckusick int dcmsoftCAR[NDCM]; 6541480Smckusick struct dcmdevice *dcm_addr[NDCM]; 6641480Smckusick struct isr dcmisr[NDCM]; 6741480Smckusick 6841480Smckusick struct speedtab dcmspeedtab[] = { 6941480Smckusick 0, BR_0, 7041480Smckusick 50, BR_50, 7141480Smckusick 75, BR_75, 7241480Smckusick 110, BR_110, 7341480Smckusick 134, BR_134, 7441480Smckusick 150, BR_150, 7541480Smckusick 300, BR_300, 7641480Smckusick 600, BR_600, 7741480Smckusick 1200, BR_1200, 7841480Smckusick 1800, BR_1800, 7941480Smckusick 2400, BR_2400, 8041480Smckusick 4800, BR_4800, 8141480Smckusick 9600, BR_9600, 8241480Smckusick 19200, BR_19200, 8341480Smckusick 38400, BR_38400, 8441480Smckusick -1, -1 8541480Smckusick }; 8641480Smckusick 8742354Smckusick /* u-sec per character based on baudrate (assumes 1 start/8 data/1 stop bit) */ 8842354Smckusick #define DCM_USPERCH(s) (10000000 / (s)) 8942354Smckusick 9042354Smckusick /* 9142354Smckusick * Per board interrupt scheme. 16.7ms is the polling interrupt rate 9242354Smckusick * (16.7ms is about 550 buad, 38.4k is 72 chars in 16.7ms). 9342354Smckusick */ 9442354Smckusick #define DIS_TIMER 0 9542354Smckusick #define DIS_PERCHAR 1 9642354Smckusick #define DIS_RESET 2 9742354Smckusick 9842354Smckusick int dcmistype = -1; /* -1 == dynamic, 0 == timer, 1 == perchar */ 9942354Smckusick int dcminterval = 5; /* interval (secs) between checks */ 10042354Smckusick struct dcmischeme { 10142354Smckusick int dis_perchar; /* non-zero if interrupting per char */ 10242354Smckusick long dis_time; /* last time examined */ 10342354Smckusick int dis_intr; /* recv interrupts during last interval */ 10442354Smckusick int dis_char; /* characters read during last interval */ 10542354Smckusick } dcmischeme[NDCM]; 10642354Smckusick 10742354Smckusick /* 10842354Smckusick * Console support 10942354Smckusick */ 11042354Smckusick int dcmconsole = -1; 11142354Smckusick int dcmdefaultrate = DEFAULT_BAUD_RATE; 11242354Smckusick int dcmconbrdbusy = 0; 11342354Smckusick extern struct tty *constty; 11442354Smckusick 11542354Smckusick #ifdef KGDB 11642354Smckusick /* 11742354Smckusick * Kernel GDB support 11842354Smckusick */ 11942354Smckusick extern int kgdb_dev; 12042354Smckusick extern int kgdb_rate; 12142354Smckusick extern int kgdb_debug_init; 12242354Smckusick #endif 12342354Smckusick 12442354Smckusick /* #define IOSTATS */ 12542354Smckusick 12641480Smckusick #ifdef DEBUG 12745750Smckusick int dcmdebug = 0x0; 12841480Smckusick #define DDB_SIOERR 0x01 12941480Smckusick #define DDB_PARAM 0x02 13041480Smckusick #define DDB_INPUT 0x04 13141480Smckusick #define DDB_OUTPUT 0x08 13241480Smckusick #define DDB_INTR 0x10 13342354Smckusick #define DDB_IOCTL 0x20 13442354Smckusick #define DDB_INTSCHM 0x40 13542354Smckusick #define DDB_MODEM 0x80 13641480Smckusick #define DDB_OPENCLOSE 0x100 13742354Smckusick #endif 13841480Smckusick 13942354Smckusick #ifdef IOSTATS 14042354Smckusick #define DCMRBSIZE 94 14142354Smckusick #define DCMXBSIZE 24 14242354Smckusick 14342354Smckusick struct dcmstats { 14442354Smckusick long xints; /* # of xmit ints */ 14542354Smckusick long xchars; /* # of xmit chars */ 14642354Smckusick long xempty; /* times outq is empty in dcmstart */ 14742354Smckusick long xrestarts; /* times completed while xmitting */ 14842354Smckusick long rints; /* # of recv ints */ 14942354Smckusick long rchars; /* # of recv chars */ 15042354Smckusick long xsilo[DCMXBSIZE+2]; /* times this many chars xmit on one int */ 15142354Smckusick long rsilo[DCMRBSIZE+2]; /* times this many chars read on one int */ 15242354Smckusick } dcmstats[NDCM]; 15341480Smckusick #endif 15441480Smckusick 15541480Smckusick #define UNIT(x) minor(x) 15642354Smckusick #define BOARD(x) (((x) >> 2) & 0x3f) 15741480Smckusick #define PORT(x) ((x) & 3) 15841480Smckusick #define MKUNIT(b,p) (((b) << 2) | (p)) 15941480Smckusick 16041480Smckusick dcmprobe(hd) 16141480Smckusick register struct hp_device *hd; 16241480Smckusick { 16341480Smckusick register struct dcmdevice *dcm; 16441480Smckusick register int i; 16541480Smckusick register int timo = 0; 16642354Smckusick int s, brd, isconsole; 16741480Smckusick 16841480Smckusick dcm = (struct dcmdevice *)hd->hp_addr; 16941480Smckusick if ((dcm->dcm_rsid & 0x1f) != DCMID) 17041480Smckusick return (0); 17141480Smckusick brd = hd->hp_unit; 17242354Smckusick isconsole = (brd == BOARD(dcmconsole)); 17342354Smckusick /* 17442354Smckusick * XXX selected console device (CONSUNIT) as determined by 17542354Smckusick * dcmcnprobe does not agree with logical numbering imposed 17642354Smckusick * by the config file (i.e. lowest address DCM is not unit 17742354Smckusick * CONSUNIT). Don't recognize this card. 17842354Smckusick */ 17942354Smckusick if (isconsole && dcm != dcm_addr[BOARD(dcmconsole)]) 18042354Smckusick return(0); 18142354Smckusick 18242354Smckusick /* 18342354Smckusick * Empirically derived self-test magic 18442354Smckusick */ 18541480Smckusick s = spltty(); 18641480Smckusick dcm->dcm_rsid = DCMRS; 18741480Smckusick DELAY(50000); /* 5000 is not long enough */ 18841480Smckusick dcm->dcm_rsid = 0; 18941480Smckusick dcm->dcm_ic = IC_IE; 19041480Smckusick dcm->dcm_cr = CR_SELFT; 19142354Smckusick while ((dcm->dcm_ic & IC_IR) == 0) 19242354Smckusick if (++timo == 20000) 19341480Smckusick return(0); 19441480Smckusick DELAY(50000) /* XXX why is this needed ???? */ 19542354Smckusick while ((dcm->dcm_iir & IIR_SELFT) == 0) 19642354Smckusick if (++timo == 400000) 19741480Smckusick return(0); 19841480Smckusick DELAY(50000) /* XXX why is this needed ???? */ 19941480Smckusick if (dcm->dcm_stcon != ST_OK) { 20042354Smckusick if (!isconsole) 20142354Smckusick printf("dcm%d: self test failed: %x\n", 20242354Smckusick brd, dcm->dcm_stcon); 20341480Smckusick return(0); 20441480Smckusick } 20541480Smckusick dcm->dcm_ic = IC_ID; 20641480Smckusick splx(s); 20741480Smckusick 20841480Smckusick hd->hp_ipl = DCMIPL(dcm->dcm_ic); 20942354Smckusick dcm_addr[brd] = dcm; 21042354Smckusick dcm_active |= 1 << brd; 21142354Smckusick dcmsoftCAR[brd] = hd->hp_flags; 21241480Smckusick dcmisr[brd].isr_ipl = hd->hp_ipl; 21341480Smckusick dcmisr[brd].isr_arg = brd; 21441480Smckusick dcmisr[brd].isr_intr = dcmintr; 21541480Smckusick isrlink(&dcmisr[brd]); 21642354Smckusick #ifdef KGDB 21742354Smckusick if (major(kgdb_dev) == 2 && BOARD(kgdb_dev) == brd) { 21842354Smckusick if (dcmconsole == UNIT(kgdb_dev)) 21942354Smckusick kgdb_dev = -1; /* can't debug over console port */ 22042354Smckusick else { 22142354Smckusick (void) dcminit(kgdb_dev, kgdb_rate); 22242354Smckusick if (kgdb_debug_init) { 22342354Smckusick printf("dcm%d: kgdb waiting...", 22442354Smckusick UNIT(kgdb_dev)); 22542354Smckusick /* trap into kgdb */ 22642354Smckusick asm("trap #15;"); 22742354Smckusick printf("connected.\n"); 22842354Smckusick } else 22942354Smckusick printf("dcm%d: kgdb enabled\n", 23042354Smckusick UNIT(kgdb_dev)); 23142354Smckusick } 23242354Smckusick } 23342354Smckusick #endif 23442354Smckusick if (dcmistype == DIS_TIMER) 23542354Smckusick dcmsetischeme(brd, DIS_RESET|DIS_TIMER); 23642354Smckusick else 23742354Smckusick dcmsetischeme(brd, DIS_RESET|DIS_PERCHAR); 23845750Smckusick 23945750Smckusick /* load pointers to modem control */ 24045750Smckusick dcm_modem[MKUNIT(brd, 0)] = &dcm->dcm_modem0; 24145750Smckusick dcm_modem[MKUNIT(brd, 1)] = &dcm->dcm_modem1; 24245750Smckusick dcm_modem[MKUNIT(brd, 2)] = &dcm->dcm_modem2; 24345750Smckusick dcm_modem[MKUNIT(brd, 3)] = &dcm->dcm_modem3; 24445750Smckusick /* set DCD (modem) and CTS (flow control) on all ports */ 24545750Smckusick for (i = 0; i < 4; i++) 24645750Smckusick dcm_modem[MKUNIT(brd, i)]->mdmmsk = MI_CD|MI_CTS; 24745750Smckusick 24842354Smckusick dcm->dcm_ic = IC_IE; /* turn all interrupts on */ 24941480Smckusick /* 25041480Smckusick * Need to reset baud rate, etc. of next print so reset dcmconsole. 25141480Smckusick * Also make sure console is always "hardwired" 25241480Smckusick */ 25342354Smckusick if (isconsole) { 25442354Smckusick dcmconsole = -1; 25541480Smckusick dcmsoftCAR[brd] |= (1 << PORT(dcmconsole)); 25641480Smckusick } 25741480Smckusick return (1); 25841480Smckusick } 25941480Smckusick 26041480Smckusick dcmopen(dev, flag) 26141480Smckusick dev_t dev; 26241480Smckusick { 26341480Smckusick register struct tty *tp; 26441480Smckusick register int unit, brd; 26544762Skarels int error = 0; 26641480Smckusick 26741480Smckusick unit = UNIT(dev); 26841480Smckusick brd = BOARD(unit); 26942354Smckusick if (unit >= NDCMLINE || (dcm_active & (1 << brd)) == 0) 27041480Smckusick return (ENXIO); 27142354Smckusick #ifdef KGDB 27242354Smckusick if (unit == UNIT(kgdb_dev)) 27342354Smckusick return (EBUSY); 27442354Smckusick #endif 27541480Smckusick tp = &dcm_tty[unit]; 27641480Smckusick tp->t_oproc = dcmstart; 27742354Smckusick tp->t_param = dcmparam; 27841480Smckusick tp->t_dev = dev; 27941480Smckusick if ((tp->t_state & TS_ISOPEN) == 0) { 28042950Smarc tp->t_state |= TS_WOPEN; 28141480Smckusick ttychars(tp); 28241480Smckusick tp->t_iflag = TTYDEF_IFLAG; 28341480Smckusick tp->t_oflag = TTYDEF_OFLAG; 28441480Smckusick tp->t_cflag = TTYDEF_CFLAG; 28541480Smckusick tp->t_lflag = TTYDEF_LFLAG; 28641480Smckusick tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 28742354Smckusick (void) dcmparam(tp, &tp->t_termios); 28841480Smckusick ttsetwater(tp); 28941480Smckusick } else if (tp->t_state&TS_XCLUDE && u.u_uid != 0) 29041480Smckusick return (EBUSY); 29145750Smckusick (void) dcmmctl(dev, MO_ON, DMSET); /* enable port */ 29241480Smckusick if (dcmsoftCAR[brd] & (1 << PORT(unit))) 29341480Smckusick tp->t_state |= TS_CARR_ON; 29441480Smckusick else if (dcmmctl(dev, MO_OFF, DMGET) & MI_CD) 29541480Smckusick tp->t_state |= TS_CARR_ON; 29645750Smckusick #ifdef DEBUG 29745750Smckusick if (dcmdebug & DDB_MODEM) 29845750Smckusick printf("dcm%d: dcmopen port %d softcarr %c\n", 29945750Smckusick brd, unit, (tp->t_state & TS_CARR_ON) ? '1' : '0'); 30045750Smckusick #endif 30141480Smckusick (void) spltty(); 30244295Shibler while ((flag&O_NONBLOCK) == 0 && (tp->t_cflag&CLOCAL) == 0 && 30341480Smckusick (tp->t_state & TS_CARR_ON) == 0) { 30441480Smckusick tp->t_state |= TS_WOPEN; 30544295Shibler if (error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH, 30644295Shibler ttopen, 0)) 30744295Shibler break; 30841480Smckusick } 30941480Smckusick (void) spl0(); 31045750Smckusick 31141480Smckusick #ifdef DEBUG 31241480Smckusick if (dcmdebug & DDB_OPENCLOSE) 31341480Smckusick printf("dcmopen: u %x st %x fl %x\n", 31441480Smckusick unit, tp->t_state, tp->t_flags); 31541480Smckusick #endif 31644295Shibler if (error == 0) 31744295Shibler error = (*linesw[tp->t_line].l_open)(dev, tp); 31844295Shibler return (error); 31941480Smckusick } 32041480Smckusick 32141480Smckusick /*ARGSUSED*/ 32241480Smckusick dcmclose(dev, flag) 32341480Smckusick dev_t dev; 32441480Smckusick { 32541480Smckusick register struct tty *tp; 32641480Smckusick int unit; 32741480Smckusick 32841480Smckusick unit = UNIT(dev); 32941480Smckusick tp = &dcm_tty[unit]; 33041480Smckusick (*linesw[tp->t_line].l_close)(tp); 33141480Smckusick if (tp->t_cflag&HUPCL || tp->t_state&TS_WOPEN || 33241480Smckusick (tp->t_state&TS_ISOPEN) == 0) 33341480Smckusick (void) dcmmctl(dev, MO_OFF, DMSET); 33441480Smckusick #ifdef DEBUG 33541480Smckusick if (dcmdebug & DDB_OPENCLOSE) 33641480Smckusick printf("dcmclose: u %x st %x fl %x\n", 33741480Smckusick unit, tp->t_state, tp->t_flags); 33841480Smckusick #endif 33941480Smckusick ttyclose(tp); 34041480Smckusick return(0); 34141480Smckusick } 34241480Smckusick 34341480Smckusick dcmread(dev, uio, flag) 34441480Smckusick dev_t dev; 34541480Smckusick struct uio *uio; 34641480Smckusick { 34741480Smckusick register struct tty *tp; 34841480Smckusick 34941480Smckusick tp = &dcm_tty[UNIT(dev)]; 35041480Smckusick return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); 35141480Smckusick } 35241480Smckusick 35341480Smckusick dcmwrite(dev, uio, flag) 35441480Smckusick dev_t dev; 35541480Smckusick struct uio *uio; 35641480Smckusick { 35741480Smckusick int unit = UNIT(dev); 35841480Smckusick register struct tty *tp; 35941480Smckusick 36041480Smckusick tp = &dcm_tty[unit]; 36142354Smckusick /* 36242354Smckusick * XXX we disallow virtual consoles if the physical console is 36342354Smckusick * a serial port. This is in case there is a display attached that 36442354Smckusick * is not the console. In that situation we don't need/want the X 36542354Smckusick * server taking over the console. 36642354Smckusick */ 36742354Smckusick if (constty && unit == dcmconsole) 36842354Smckusick constty = NULL; 36941480Smckusick return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); 37041480Smckusick } 37141480Smckusick 37241480Smckusick dcmintr(brd) 37341480Smckusick register int brd; 37441480Smckusick { 37542354Smckusick register struct dcmdevice *dcm = dcm_addr[brd]; 37642354Smckusick register struct dcmischeme *dis; 37745750Smckusick register int unit = MKUNIT(brd, 0); 37845750Smckusick register int code, i; 37945750Smckusick int pcnd[4], mcode, mcnd[4]; 38041480Smckusick 38142354Smckusick /* 38242354Smckusick * Do all guarded register accesses right off to minimize 38342354Smckusick * block out of hardware. 38442354Smckusick */ 38541480Smckusick SEM_LOCK(dcm); 38641480Smckusick if ((dcm->dcm_ic & IC_IR) == 0) { 38741480Smckusick SEM_UNLOCK(dcm); 38841480Smckusick return(0); 38941480Smckusick } 39041480Smckusick for (i = 0; i < 4; i++) { 39141480Smckusick pcnd[i] = dcm->dcm_icrtab[i].dcm_data; 39241480Smckusick dcm->dcm_icrtab[i].dcm_data = 0; 39345750Smckusick mcnd[i] = dcm_modem[unit+i]->mdmin; 39441480Smckusick } 39541480Smckusick code = dcm->dcm_iir & IIR_MASK; 39642354Smckusick dcm->dcm_iir = 0; /* XXX doc claims read clears interrupt?! */ 39745750Smckusick mcode = dcm->dcm_modemintr; 39845750Smckusick dcm->dcm_modemintr = 0; 39941480Smckusick SEM_UNLOCK(dcm); 40041480Smckusick 40141480Smckusick #ifdef DEBUG 40245750Smckusick if (dcmdebug & DDB_INTR) { 40345750Smckusick printf("dcmintr(%d): iir %x pc %x/%x/%x/%x ", 40445750Smckusick brd, code, pcnd[0], pcnd[1], pcnd[2], pcnd[3]); 40545750Smckusick printf("miir %x mc %x/%x/%x/%x\n", 40645750Smckusick mcode, mcnd[0], mcnd[1], mcnd[2], mcnd[3]); 40745750Smckusick } 40841480Smckusick #endif 40942354Smckusick if (code & IIR_TIMEO) 41042354Smckusick dcmrint(brd, dcm); 41141480Smckusick if (code & IIR_PORT0) 41245750Smckusick dcmpint(unit+0, pcnd[0], dcm); 41341480Smckusick if (code & IIR_PORT1) 41445750Smckusick dcmpint(unit+1, pcnd[1], dcm); 41541480Smckusick if (code & IIR_PORT2) 41645750Smckusick dcmpint(unit+2, pcnd[2], dcm); 41741480Smckusick if (code & IIR_PORT3) 41845750Smckusick dcmpint(unit+3, pcnd[3], dcm); 41945750Smckusick if (code & IIR_MODM) { 42045750Smckusick if (mcode == 0 || mcode & 0x1) /* mcode==0 -> 98642 board */ 42145750Smckusick dcmmint(unit+0, mcnd[0], dcm); 42245750Smckusick if (mcode & 0x2) 42345750Smckusick dcmmint(unit+1, mcnd[1], dcm); 42445750Smckusick if (mcode & 0x4) 42545750Smckusick dcmmint(unit+2, mcnd[2], dcm); 42645750Smckusick if (mcode & 0x8) 42745750Smckusick dcmmint(unit+3, mcnd[3], dcm); 42845750Smckusick } 42941480Smckusick 43042354Smckusick dis = &dcmischeme[brd]; 43141480Smckusick /* 43242354Smckusick * Chalk up a receiver interrupt if the timer running or one of 43342354Smckusick * the ports reports a special character interrupt. 43441480Smckusick */ 43542354Smckusick if ((code & IIR_TIMEO) || 43642354Smckusick ((pcnd[0]|pcnd[1]|pcnd[2]|pcnd[3]) & IT_SPEC)) 43742354Smckusick dis->dis_intr++; 43842354Smckusick /* 43942354Smckusick * See if it is time to check/change the interrupt rate. 44042354Smckusick */ 44142354Smckusick if (dcmistype < 0 && 44245750Smckusick (i = time.tv_sec - dis->dis_time) >= dcminterval) { 44341480Smckusick /* 44442354Smckusick * If currently per-character and averaged over 70 interrupts 44542354Smckusick * per-second (66 is threshold of 600 baud) in last interval, 44642354Smckusick * switch to timer mode. 44742354Smckusick * 44842354Smckusick * XXX decay counts ala load average to avoid spikes? 44941480Smckusick */ 45045750Smckusick if (dis->dis_perchar && dis->dis_intr > 70 * i) 45142354Smckusick dcmsetischeme(brd, DIS_TIMER); 45242354Smckusick /* 45342354Smckusick * If currently using timer and had more interrupts than 45442354Smckusick * received characters in the last interval, switch back 45542354Smckusick * to per-character. Note that after changing to per-char 45642354Smckusick * we must process any characters already in the queue 45742354Smckusick * since they may have arrived before the bitmap was setup. 45842354Smckusick * 45942354Smckusick * XXX decay counts? 46042354Smckusick */ 46142354Smckusick else if (!dis->dis_perchar && dis->dis_intr > dis->dis_char) { 46242354Smckusick dcmsetischeme(brd, DIS_PERCHAR); 46341480Smckusick dcmrint(brd, dcm); 46441480Smckusick } 46542354Smckusick dis->dis_intr = dis->dis_char = 0; 46642354Smckusick dis->dis_time = time.tv_sec; 46742354Smckusick } 46841480Smckusick return(1); 46941480Smckusick } 47041480Smckusick 47141480Smckusick /* 47241480Smckusick * Port interrupt. Can be two things: 47341480Smckusick * First, it might be a special character (exception interrupt); 47441480Smckusick * Second, it may be a buffer empty (transmit interrupt); 47541480Smckusick */ 47641480Smckusick dcmpint(unit, code, dcm) 47741480Smckusick int unit, code; 47842354Smckusick struct dcmdevice *dcm; 47941480Smckusick { 48042354Smckusick struct tty *tp = &dcm_tty[unit]; 48141480Smckusick 48242354Smckusick if (code & IT_SPEC) 48342354Smckusick dcmreadbuf(unit, dcm, tp); 48441480Smckusick if (code & IT_TX) 48542354Smckusick dcmxint(unit, dcm, tp); 48641480Smckusick } 48741480Smckusick 48841480Smckusick dcmrint(brd, dcm) 48941480Smckusick int brd; 49041480Smckusick register struct dcmdevice *dcm; 49141480Smckusick { 49242354Smckusick register int i, unit; 49341480Smckusick register struct tty *tp; 49441480Smckusick 49541480Smckusick unit = MKUNIT(brd, 0); 49641480Smckusick tp = &dcm_tty[unit]; 49742354Smckusick for (i = 0; i < 4; i++, tp++, unit++) 49842354Smckusick dcmreadbuf(unit, dcm, tp); 49941480Smckusick } 50041480Smckusick 50142354Smckusick dcmreadbuf(unit, dcm, tp) 50241480Smckusick int unit; 50341480Smckusick register struct dcmdevice *dcm; 50441480Smckusick register struct tty *tp; 50541480Smckusick { 50642354Smckusick int port = PORT(unit); 50742354Smckusick register struct dcmpreg *pp = dcm_preg(dcm, port); 50842354Smckusick register struct dcmrfifo *fifo; 50941480Smckusick register int c, stat; 51041480Smckusick register unsigned head; 51142354Smckusick int nch = 0; 51242354Smckusick #ifdef IOSTATS 51342354Smckusick struct dcmstats *dsp = &dcmstats[BOARD(unit)]; 51441480Smckusick 51542354Smckusick dsp->rints++; 51642354Smckusick #endif 51743407Shibler if ((tp->t_state & TS_ISOPEN) == 0) { 51842354Smckusick #ifdef KGDB 51942354Smckusick if (unit == UNIT(kgdb_dev) && 52042354Smckusick (head = pp->r_head & RX_MASK) != (pp->r_tail & RX_MASK) && 52142354Smckusick dcm->dcm_rfifos[3-port][head>>1].data_char == '!') { 52242354Smckusick pp->r_head = (head + 2) & RX_MASK; 52342354Smckusick printf("kgdb trap from dcm%d\n", unit); 52442354Smckusick /* trap into kgdb */ 52542354Smckusick asm("trap #15;"); 52642354Smckusick return; 52742354Smckusick } 52842354Smckusick #endif 52942354Smckusick pp->r_head = pp->r_tail & RX_MASK; 53042354Smckusick return; 53142354Smckusick } 53241480Smckusick 53342354Smckusick head = pp->r_head & RX_MASK; 53442354Smckusick fifo = &dcm->dcm_rfifos[3-port][head>>1]; 53542354Smckusick /* 53642354Smckusick * XXX upper bound on how many chars we will take in one swallow? 53742354Smckusick */ 53842354Smckusick while (head != (pp->r_tail & RX_MASK)) { 53942354Smckusick /* 54042354Smckusick * Get character/status and update head pointer as fast 54142354Smckusick * as possible to make room for more characters. 54242354Smckusick */ 54342354Smckusick c = fifo->data_char; 54442354Smckusick stat = fifo->data_stat; 54541480Smckusick head = (head + 2) & RX_MASK; 54642354Smckusick pp->r_head = head; 54742354Smckusick fifo = head ? fifo+1 : &dcm->dcm_rfifos[3-port][0]; 54842354Smckusick nch++; 54941480Smckusick 55041480Smckusick #ifdef DEBUG 55141480Smckusick if (dcmdebug & DDB_INPUT) 55242354Smckusick printf("dcmreadbuf(%d): c%x('%c') s%x f%x h%x t%x\n", 55342354Smckusick unit, c&0xFF, c, stat&0xFF, 55442354Smckusick tp->t_flags, head, pp->r_tail); 55541480Smckusick #endif 55642354Smckusick /* 55742354Smckusick * Check for and handle errors 55842354Smckusick */ 55942354Smckusick if (stat & RD_MASK) { 56041480Smckusick #ifdef DEBUG 56142354Smckusick if (dcmdebug & (DDB_INPUT|DDB_SIOERR)) 56242354Smckusick printf("dcmreadbuf(%d): err: c%x('%c') s%x\n", 56342354Smckusick unit, stat, c&0xFF, c); 56441480Smckusick #endif 56541480Smckusick if (stat & (RD_BD | RD_FE)) 56641480Smckusick c |= TTY_FE; 56741480Smckusick else if (stat & RD_PE) 56841480Smckusick c |= TTY_PE; 56941480Smckusick else if (stat & RD_OVF) 57041480Smckusick log(LOG_WARNING, 57142354Smckusick "dcm%d: silo overflow\n", unit); 57241480Smckusick else if (stat & RD_OE) 57341480Smckusick log(LOG_WARNING, 57442354Smckusick "dcm%d: uart overflow\n", unit); 57541480Smckusick } 57641480Smckusick (*linesw[tp->t_line].l_rint)(c, tp); 57741480Smckusick } 57842354Smckusick dcmischeme[BOARD(unit)].dis_char += nch; 57942354Smckusick #ifdef IOSTATS 58042354Smckusick dsp->rchars += nch; 58142354Smckusick if (nch <= DCMRBSIZE) 58242354Smckusick dsp->rsilo[nch]++; 58341480Smckusick else 58442354Smckusick dsp->rsilo[DCMRBSIZE+1]++; 58541480Smckusick #endif 58641480Smckusick } 58741480Smckusick 58842354Smckusick dcmxint(unit, dcm, tp) 58941480Smckusick int unit; 59041480Smckusick struct dcmdevice *dcm; 59142354Smckusick register struct tty *tp; 59241480Smckusick { 59341480Smckusick tp->t_state &= ~TS_BUSY; 59441480Smckusick if (tp->t_state & TS_FLUSH) 59541480Smckusick tp->t_state &= ~TS_FLUSH; 59641480Smckusick if (tp->t_line) 59741480Smckusick (*linesw[tp->t_line].l_start)(tp); 59841480Smckusick else 59941480Smckusick dcmstart(tp); 60041480Smckusick } 60141480Smckusick 60241480Smckusick dcmmint(unit, mcnd, dcm) 60341480Smckusick register int unit; 60441480Smckusick register struct dcmdevice *dcm; 60541480Smckusick int mcnd; 60641480Smckusick { 60741480Smckusick register struct tty *tp; 60844317Shibler int delta; 60941480Smckusick 61041480Smckusick #ifdef DEBUG 61142354Smckusick if (dcmdebug & DDB_MODEM) 61245750Smckusick printf("dcmmint: port %d mcnd %x mcndlast %x\n", 61344317Shibler unit, mcnd, mcndlast[unit]); 61442354Smckusick #endif 61541480Smckusick tp = &dcm_tty[unit]; 61644317Shibler delta = mcnd ^ mcndlast[unit]; 61744317Shibler mcndlast[unit] = mcnd; 61844317Shibler if ((delta & MI_CD) && 61944317Shibler (dcmsoftCAR[BOARD(unit)] & (1 << PORT(unit))) == 0) { 62041480Smckusick if (mcnd & MI_CD) 62144317Shibler (void)(*linesw[tp->t_line].l_modem)(tp, 1); 62241480Smckusick else if ((*linesw[tp->t_line].l_modem)(tp, 0) == 0) { 62345750Smckusick dcm_modem[unit]->mdmout &= ~(MO_DTR|MO_RTS); 62441480Smckusick SEM_LOCK(dcm); 62545750Smckusick dcm->dcm_modemchng |= 1<<(unit & 3); 62641480Smckusick dcm->dcm_cr |= CR_MODM; 62741480Smckusick SEM_UNLOCK(dcm); 62842354Smckusick DELAY(10); /* time to change lines */ 62941480Smckusick } 63044317Shibler } else if ((delta & MI_CTS) && 63144317Shibler (tp->t_state & TS_ISOPEN) && (tp->t_flags & CRTSCTS)) { 63244317Shibler if (mcnd & MI_CTS) { 63344317Shibler tp->t_state &= ~TS_TTSTOP; 63444317Shibler ttstart(tp); 63544317Shibler } else 63644317Shibler tp->t_state |= TS_TTSTOP; /* inline dcmstop */ 63741480Smckusick } 63841480Smckusick } 63941480Smckusick 64041480Smckusick dcmioctl(dev, cmd, data, flag) 64141480Smckusick dev_t dev; 64241480Smckusick caddr_t data; 64341480Smckusick { 64441480Smckusick register struct tty *tp; 64541480Smckusick register int unit = UNIT(dev); 64641480Smckusick register struct dcmdevice *dcm; 64741480Smckusick register int port; 64842354Smckusick int error, s; 64941480Smckusick 65041480Smckusick #ifdef DEBUG 65141480Smckusick if (dcmdebug & DDB_IOCTL) 65241480Smckusick printf("dcmioctl: unit %d cmd %x data %x flag %x\n", 65341480Smckusick unit, cmd, *data, flag); 65441480Smckusick #endif 65541480Smckusick tp = &dcm_tty[unit]; 65641480Smckusick error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag); 65741480Smckusick if (error >= 0) 65841480Smckusick return (error); 65941480Smckusick error = ttioctl(tp, cmd, data, flag); 66041480Smckusick if (error >= 0) 66141480Smckusick return (error); 66241480Smckusick 66341480Smckusick port = PORT(unit); 66441480Smckusick dcm = dcm_addr[BOARD(unit)]; 66541480Smckusick switch (cmd) { 66641480Smckusick case TIOCSBRK: 66742354Smckusick /* 66842354Smckusick * Wait for transmitter buffer to empty 66942354Smckusick */ 67042354Smckusick s = spltty(); 67142354Smckusick while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr) 67242354Smckusick DELAY(DCM_USPERCH(tp->t_ospeed)); 67341480Smckusick SEM_LOCK(dcm); 67442354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_BRK; 67542354Smckusick dcm->dcm_cr |= (1 << port); /* start break */ 67641480Smckusick SEM_UNLOCK(dcm); 67742354Smckusick splx(s); 67841480Smckusick break; 67941480Smckusick 68041480Smckusick case TIOCCBRK: 68141480Smckusick SEM_LOCK(dcm); 68242354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_BRK; 68342354Smckusick dcm->dcm_cr |= (1 << port); /* end break */ 68441480Smckusick SEM_UNLOCK(dcm); 68541480Smckusick break; 68641480Smckusick 68741480Smckusick case TIOCSDTR: 68841480Smckusick (void) dcmmctl(dev, MO_ON, DMBIS); 68941480Smckusick break; 69041480Smckusick 69141480Smckusick case TIOCCDTR: 69241480Smckusick (void) dcmmctl(dev, MO_ON, DMBIC); 69341480Smckusick break; 69441480Smckusick 69541480Smckusick case TIOCMSET: 69641480Smckusick (void) dcmmctl(dev, *(int *)data, DMSET); 69741480Smckusick break; 69841480Smckusick 69941480Smckusick case TIOCMBIS: 70041480Smckusick (void) dcmmctl(dev, *(int *)data, DMBIS); 70141480Smckusick break; 70241480Smckusick 70341480Smckusick case TIOCMBIC: 70441480Smckusick (void) dcmmctl(dev, *(int *)data, DMBIC); 70541480Smckusick break; 70641480Smckusick 70741480Smckusick case TIOCMGET: 70841480Smckusick *(int *)data = dcmmctl(dev, 0, DMGET); 70941480Smckusick break; 71041480Smckusick 71141480Smckusick default: 71241480Smckusick return (ENOTTY); 71341480Smckusick } 71441480Smckusick return (0); 71541480Smckusick } 71641480Smckusick 71741480Smckusick dcmparam(tp, t) 71841480Smckusick register struct tty *tp; 71941480Smckusick register struct termios *t; 72041480Smckusick { 72141480Smckusick register struct dcmdevice *dcm; 72242354Smckusick register int port, mode, cflag = t->c_cflag; 72341480Smckusick int ospeed = ttspeedtab(t->c_ospeed, dcmspeedtab); 72442354Smckusick 72541480Smckusick /* check requested parameters */ 72641480Smckusick if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)) 72741480Smckusick return(EINVAL); 72841480Smckusick /* and copy to tty */ 72941480Smckusick tp->t_ispeed = t->c_ispeed; 73041480Smckusick tp->t_ospeed = t->c_ospeed; 73141480Smckusick tp->t_cflag = cflag; 73241480Smckusick if (ospeed == 0) { 73342354Smckusick (void) dcmmctl(UNIT(tp->t_dev), MO_OFF, DMSET); 73442354Smckusick return(0); 73541480Smckusick } 73642354Smckusick 73742354Smckusick mode = 0; 73841480Smckusick switch (cflag&CSIZE) { 73941480Smckusick case CS5: 74041480Smckusick mode = LC_5BITS; break; 74141480Smckusick case CS6: 74241480Smckusick mode = LC_6BITS; break; 74341480Smckusick case CS7: 74441480Smckusick mode = LC_7BITS; break; 74541480Smckusick case CS8: 74641480Smckusick mode = LC_8BITS; break; 74741480Smckusick } 74841480Smckusick if (cflag&PARENB) { 74941480Smckusick if (cflag&PARODD) 75041480Smckusick mode |= LC_PODD; 75141480Smckusick else 75241480Smckusick mode |= LC_PEVEN; 75341480Smckusick } 75441480Smckusick if (cflag&CSTOPB) 75541480Smckusick mode |= LC_2STOP; 75641480Smckusick else 75741480Smckusick mode |= LC_1STOP; 75841480Smckusick #ifdef DEBUG 75941480Smckusick if (dcmdebug & DDB_PARAM) 76042354Smckusick printf("dcmparam(%d): cflag %x mode %x speed %d uperch %d\n", 76142354Smckusick UNIT(tp->t_dev), cflag, mode, tp->t_ospeed, 76242354Smckusick DCM_USPERCH(tp->t_ospeed)); 76341480Smckusick #endif 76442354Smckusick 76542354Smckusick port = PORT(tp->t_dev); 76642354Smckusick dcm = dcm_addr[BOARD(tp->t_dev)]; 76742354Smckusick /* 76842354Smckusick * Wait for transmitter buffer to empty. 76942354Smckusick */ 77041480Smckusick while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr) 77142354Smckusick DELAY(DCM_USPERCH(tp->t_ospeed)); 77242354Smckusick /* 77342354Smckusick * Make changes known to hardware. 77442354Smckusick */ 77542354Smckusick dcm->dcm_data[port].dcm_baud = ospeed; 77641480Smckusick dcm->dcm_data[port].dcm_conf = mode; 77741480Smckusick SEM_LOCK(dcm); 77842354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_CON; 77942354Smckusick dcm->dcm_cr |= (1 << port); 78041480Smckusick SEM_UNLOCK(dcm); 78142354Smckusick /* 78242354Smckusick * Delay for config change to take place. Weighted by buad. 78342354Smckusick * XXX why do we do this? 78442354Smckusick */ 78542354Smckusick DELAY(16 * DCM_USPERCH(tp->t_ospeed)); 78642354Smckusick return(0); 78741480Smckusick } 78841480Smckusick 78941480Smckusick dcmstart(tp) 79041480Smckusick register struct tty *tp; 79141480Smckusick { 79241480Smckusick register struct dcmdevice *dcm; 79342354Smckusick register struct dcmpreg *pp; 79442354Smckusick register struct dcmtfifo *fifo; 79542354Smckusick register char *bp; 79642354Smckusick register unsigned tail, next; 79742354Smckusick register int port, nch; 79842354Smckusick unsigned head; 79942354Smckusick char buf[16]; 80042354Smckusick int s; 80142354Smckusick #ifdef IOSTATS 80242354Smckusick struct dcmstats *dsp = &dcmstats[BOARD(tp->t_dev)]; 80342354Smckusick int tch = 0; 80442354Smckusick #endif 80542354Smckusick 80641480Smckusick s = spltty(); 80742354Smckusick #ifdef IOSTATS 80842354Smckusick dsp->xints++; 80942354Smckusick #endif 81041480Smckusick #ifdef DEBUG 81141480Smckusick if (dcmdebug & DDB_OUTPUT) 81242354Smckusick printf("dcmstart(%d): state %x flags %x outcc %d\n", 81342354Smckusick UNIT(tp->t_dev), tp->t_state, tp->t_flags, 81442354Smckusick tp->t_outq.c_cc); 81541480Smckusick #endif 81641480Smckusick if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) 81741480Smckusick goto out; 81841480Smckusick if (tp->t_outq.c_cc <= tp->t_lowat) { 81941480Smckusick if (tp->t_state&TS_ASLEEP) { 82041480Smckusick tp->t_state &= ~TS_ASLEEP; 82141480Smckusick wakeup((caddr_t)&tp->t_outq); 82241480Smckusick } 82341480Smckusick if (tp->t_wsel) { 82441480Smckusick selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL); 82541480Smckusick tp->t_wsel = 0; 82641480Smckusick tp->t_state &= ~TS_WCOLL; 82741480Smckusick } 82841480Smckusick } 82942354Smckusick if (tp->t_outq.c_cc == 0) { 83042354Smckusick #ifdef IOSTATS 83142354Smckusick dsp->xempty++; 83242354Smckusick #endif 83342354Smckusick goto out; 83442354Smckusick } 83542354Smckusick 83642354Smckusick dcm = dcm_addr[BOARD(tp->t_dev)]; 83742354Smckusick port = PORT(tp->t_dev); 83842354Smckusick pp = dcm_preg(dcm, port); 83942354Smckusick tail = pp->t_tail & TX_MASK; 84042354Smckusick next = (tail + 1) & TX_MASK; 84142354Smckusick head = pp->t_head & TX_MASK; 84242354Smckusick if (head == next) 84342354Smckusick goto out; 84442354Smckusick fifo = &dcm->dcm_tfifos[3-port][tail]; 84542354Smckusick again: 84642354Smckusick nch = q_to_b(&tp->t_outq, buf, (head - next) & TX_MASK); 84742354Smckusick #ifdef IOSTATS 84842354Smckusick tch += nch; 84942354Smckusick #endif 85041480Smckusick #ifdef DEBUG 85142354Smckusick if (dcmdebug & DDB_OUTPUT) 85242354Smckusick printf("\thead %x tail %x nch %d\n", head, tail, nch); 85341480Smckusick #endif 85442354Smckusick /* 85542354Smckusick * Loop transmitting all the characters we can. 85642354Smckusick */ 85742354Smckusick for (bp = buf; --nch >= 0; bp++) { 85842354Smckusick fifo->data_char = *bp; 85942354Smckusick pp->t_tail = next; 86042354Smckusick /* 86142354Smckusick * If this is the first character, 86242354Smckusick * get the hardware moving right now. 86342354Smckusick */ 86442354Smckusick if (bp == buf) { 86542354Smckusick tp->t_state |= TS_BUSY; 86642354Smckusick SEM_LOCK(dcm); 86742354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_TX; 86842354Smckusick dcm->dcm_cr |= (1 << port); 86942354Smckusick SEM_UNLOCK(dcm); 87042354Smckusick } 87141480Smckusick tail = next; 87242354Smckusick fifo = tail ? fifo+1 : &dcm->dcm_tfifos[3-port][0]; 87342354Smckusick next = (next + 1) & TX_MASK; 87441480Smckusick } 87542354Smckusick /* 87642354Smckusick * Head changed while we were loading the buffer, 87742354Smckusick * go back and load some more if we can. 87842354Smckusick */ 87942354Smckusick if (tp->t_outq.c_cc && head != (pp->t_head & TX_MASK)) { 88042354Smckusick #ifdef IOSTATS 88142354Smckusick dsp->xrestarts++; 88242354Smckusick #endif 88342354Smckusick head = pp->t_head & TX_MASK; 88442354Smckusick goto again; 88542354Smckusick } 88642354Smckusick /* 88742354Smckusick * Kick it one last time in case it finished while we were 88844317Shibler * loading the last bunch. 88942354Smckusick */ 89042354Smckusick if (bp > &buf[1]) { 89141480Smckusick tp->t_state |= TS_BUSY; 89241480Smckusick SEM_LOCK(dcm); 89342354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_TX; 89442354Smckusick dcm->dcm_cr |= (1 << port); 89542354Smckusick SEM_UNLOCK(dcm); 89642354Smckusick } 89741480Smckusick #ifdef DEBUG 89841480Smckusick if (dcmdebug & DDB_INTR) 89943407Shibler printf("dcmstart(%d): head %x tail %x outqcc %d\n", 90043407Shibler UNIT(tp->t_dev), head, tail, tp->t_outq.c_cc); 90141480Smckusick #endif 90241480Smckusick out: 90342354Smckusick #ifdef IOSTATS 90442354Smckusick dsp->xchars += tch; 90542354Smckusick if (tch <= DCMXBSIZE) 90642354Smckusick dsp->xsilo[tch]++; 90742354Smckusick else 90842354Smckusick dsp->xsilo[DCMXBSIZE+1]++; 90942354Smckusick #endif 91041480Smckusick splx(s); 91141480Smckusick } 91241480Smckusick 91341480Smckusick /* 91441480Smckusick * Stop output on a line. 91541480Smckusick */ 91641480Smckusick dcmstop(tp, flag) 91741480Smckusick register struct tty *tp; 91841480Smckusick { 91941480Smckusick int s; 92041480Smckusick 92141480Smckusick s = spltty(); 92241480Smckusick if (tp->t_state & TS_BUSY) { 92342354Smckusick /* XXX is there some way to safely stop transmission? */ 92444317Shibler if ((tp->t_state&TS_TTSTOP) == 0) 92541480Smckusick tp->t_state |= TS_FLUSH; 92641480Smckusick } 92741480Smckusick splx(s); 92841480Smckusick } 92941480Smckusick 93045750Smckusick /* 93145750Smckusick * Modem control 93245750Smckusick */ 93341480Smckusick dcmmctl(dev, bits, how) 93441480Smckusick dev_t dev; 93541480Smckusick int bits, how; 93641480Smckusick { 93741480Smckusick register struct dcmdevice *dcm; 93845750Smckusick int s, unit, hit = 0; 93941480Smckusick 94045750Smckusick unit = UNIT(dev); 94145750Smckusick #ifdef DEBUG 94245750Smckusick if (dcmdebug & DDB_MODEM) 94345750Smckusick printf("dcmmctl(%d) unit %d bits 0x%x how %x\n", 94445750Smckusick BOARD(unit), unit, bits, how); 94545750Smckusick #endif 94641480Smckusick 94745750Smckusick dcm = dcm_addr[BOARD(unit)]; 94841480Smckusick s = spltty(); 94941480Smckusick switch (how) { 95041480Smckusick 95141480Smckusick case DMSET: 95245750Smckusick dcm_modem[unit]->mdmout = bits; 95341480Smckusick hit++; 95441480Smckusick break; 95541480Smckusick 95641480Smckusick case DMBIS: 95745750Smckusick dcm_modem[unit]->mdmout |= bits; 95841480Smckusick hit++; 95941480Smckusick break; 96041480Smckusick 96141480Smckusick case DMBIC: 96245750Smckusick dcm_modem[unit]->mdmout &= ~bits; 96341480Smckusick hit++; 96441480Smckusick break; 96541480Smckusick 96641480Smckusick case DMGET: 96745750Smckusick bits = dcm_modem[unit]->mdmin; 96841480Smckusick break; 96941480Smckusick } 97041480Smckusick if (hit) { 97141480Smckusick SEM_LOCK(dcm); 97245750Smckusick dcm->dcm_modemchng |= 1<<(unit & 3); 97341480Smckusick dcm->dcm_cr |= CR_MODM; 97441480Smckusick SEM_UNLOCK(dcm); 97542354Smckusick DELAY(10); /* delay until done */ 97641480Smckusick (void) splx(s); 97741480Smckusick } 97841480Smckusick return(bits); 97941480Smckusick } 98041480Smckusick 98142354Smckusick /* 98242354Smckusick * Set board to either interrupt per-character or at a fixed interval. 98342354Smckusick */ 98442354Smckusick dcmsetischeme(brd, flags) 98542354Smckusick int brd, flags; 98641480Smckusick { 98742354Smckusick register struct dcmdevice *dcm = dcm_addr[brd]; 98842354Smckusick register struct dcmischeme *dis = &dcmischeme[brd]; 98941480Smckusick register int i; 99042354Smckusick u_char mask; 99142354Smckusick int perchar = flags & DIS_PERCHAR; 99241480Smckusick 99341480Smckusick #ifdef DEBUG 99442354Smckusick if (dcmdebug & DDB_INTSCHM) 99542354Smckusick printf("dcmsetischeme(%d, %d): cur %d, ints %d, chars %d\n", 99642354Smckusick brd, perchar, dis->dis_perchar, 99742354Smckusick dis->dis_intr, dis->dis_char); 99842354Smckusick if ((flags & DIS_RESET) == 0 && perchar == dis->dis_perchar) { 99942354Smckusick printf("dcmsetischeme(%d): redundent request %d\n", 100042354Smckusick brd, perchar); 100142354Smckusick return; 100242354Smckusick } 100342354Smckusick #endif 100442354Smckusick /* 100542354Smckusick * If perchar is non-zero, we enable interrupts on all characters 100642354Smckusick * otherwise we disable perchar interrupts and use periodic 100742354Smckusick * polling interrupts. 100842354Smckusick */ 100942354Smckusick dis->dis_perchar = perchar; 101042354Smckusick mask = perchar ? 0xf : 0x0; 101142354Smckusick for (i = 0; i < 256; i++) 101242354Smckusick dcm->dcm_bmap[i].data_data = mask; 101342354Smckusick /* 101442354Smckusick * Don't slow down tandem mode, interrupt on flow control 101542354Smckusick * chars for any port on the board. 101642354Smckusick */ 101742354Smckusick if (!perchar) { 101842354Smckusick register struct tty *tp = &dcm_tty[MKUNIT(brd, 0)]; 101942354Smckusick int c; 102042354Smckusick 102142354Smckusick for (i = 0; i < 4; i++, tp++) { 102242354Smckusick if ((c = tp->t_cc[VSTART]) != _POSIX_VDISABLE) 102342354Smckusick dcm->dcm_bmap[c].data_data |= (1 << i); 102442354Smckusick if ((c = tp->t_cc[VSTOP]) != _POSIX_VDISABLE) 102542354Smckusick dcm->dcm_bmap[c].data_data |= (1 << i); 102641480Smckusick } 102741480Smckusick } 102842354Smckusick /* 102942354Smckusick * Board starts with timer disabled so if first call is to 103042354Smckusick * set perchar mode then we don't want to toggle the timer. 103142354Smckusick */ 103242354Smckusick if (flags == (DIS_RESET|DIS_PERCHAR)) 103341480Smckusick return; 103442354Smckusick /* 103542354Smckusick * Toggle card 16.7ms interrupts (we first make sure that card 103642354Smckusick * has cleared the bit so it will see the toggle). 103742354Smckusick */ 103842354Smckusick while (dcm->dcm_cr & CR_TIMER) 103942354Smckusick ; 104041480Smckusick SEM_LOCK(dcm); 104142354Smckusick dcm->dcm_cr |= CR_TIMER; 104241480Smckusick SEM_UNLOCK(dcm); 104341480Smckusick } 104441480Smckusick 104541480Smckusick /* 104641480Smckusick * Following are all routines needed for DCM to act as console 104741480Smckusick */ 1048*45788Sbostic #include "../hp300/cons.h" 104941480Smckusick 105042354Smckusick dcmcnprobe(cp) 105142354Smckusick struct consdev *cp; 105241480Smckusick { 105342354Smckusick register struct hp_hw *hw; 105442354Smckusick int unit, i; 105542354Smckusick extern int dcmopen(); 105641480Smckusick 105742354Smckusick /* 105842354Smckusick * Implicitly assigns the lowest select code DCM card found to be 105942354Smckusick * logical unit 0 (actually CONUNIT). If your config file does 106042354Smckusick * anything different, you're screwed. 106142354Smckusick */ 106242354Smckusick for (hw = sc_table; hw->hw_type; hw++) 106342354Smckusick if (hw->hw_type == COMMDCM && !badaddr((short *)hw->hw_addr)) 106442354Smckusick break; 106542354Smckusick if (hw->hw_type != COMMDCM) { 106642354Smckusick cp->cn_pri = CN_DEAD; 106742354Smckusick return; 106842354Smckusick } 106942354Smckusick unit = CONUNIT; 107042354Smckusick dcm_addr[BOARD(CONUNIT)] = (struct dcmdevice *)hw->hw_addr; 107141480Smckusick 107242354Smckusick /* locate the major number */ 107342354Smckusick for (i = 0; i < nchrdev; i++) 107442354Smckusick if (cdevsw[i].d_open == dcmopen) 107542354Smckusick break; 107642354Smckusick 107742354Smckusick /* initialize required fields */ 107842354Smckusick cp->cn_dev = makedev(i, unit); 107942354Smckusick cp->cn_tp = &dcm_tty[unit]; 108042354Smckusick switch (dcm_addr[BOARD(unit)]->dcm_rsid) { 108142354Smckusick case DCMID: 108242354Smckusick cp->cn_pri = CN_NORMAL; 108342354Smckusick break; 108442354Smckusick case DCMID|DCMCON: 108542354Smckusick cp->cn_pri = CN_REMOTE; 108642354Smckusick break; 108742354Smckusick default: 108842354Smckusick cp->cn_pri = CN_DEAD; 108942354Smckusick break; 109042354Smckusick } 109142354Smckusick } 109242354Smckusick 109342354Smckusick dcmcninit(cp) 109442354Smckusick struct consdev *cp; 109542354Smckusick { 109642354Smckusick dcminit(cp->cn_dev, dcmdefaultrate); 109742354Smckusick dcmconsole = UNIT(cp->cn_dev); 109842354Smckusick } 109942354Smckusick 110042354Smckusick dcminit(dev, rate) 110142354Smckusick dev_t dev; 110242354Smckusick int rate; 110342354Smckusick { 110442354Smckusick register struct dcmdevice *dcm = dcm_addr[BOARD(dev)]; 110542354Smckusick int s, mode, port; 110642354Smckusick 110742354Smckusick port = PORT(dev); 110842354Smckusick mode = LC_8BITS | LC_1STOP; 110941480Smckusick s = splhigh(); 111042354Smckusick /* 111142354Smckusick * Wait for transmitter buffer to empty. 111242354Smckusick */ 111342354Smckusick while (dcm->dcm_thead[port].ptr != dcm->dcm_ttail[port].ptr) 111442354Smckusick DELAY(DCM_USPERCH(rate)); 111542354Smckusick /* 111642354Smckusick * Make changes known to hardware. 111742354Smckusick */ 111842354Smckusick dcm->dcm_data[port].dcm_baud = ttspeedtab(rate, dcmspeedtab); 111942354Smckusick dcm->dcm_data[port].dcm_conf = mode; 112042354Smckusick SEM_LOCK(dcm); 112142354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_CON; 112242354Smckusick dcm->dcm_cr |= (1 << port); 112342354Smckusick SEM_UNLOCK(dcm); 112442354Smckusick /* 112542354Smckusick * Delay for config change to take place. Weighted by buad. 112642354Smckusick * XXX why do we do this? 112742354Smckusick */ 112842354Smckusick DELAY(16 * DCM_USPERCH(rate)); 112941480Smckusick splx(s); 113041480Smckusick } 113141480Smckusick 113241480Smckusick dcmcngetc(dev) 113342354Smckusick dev_t dev; 113441480Smckusick { 113542354Smckusick register struct dcmdevice *dcm = dcm_addr[BOARD(dev)]; 113642354Smckusick register struct dcmrfifo *fifo; 113742354Smckusick register struct dcmpreg *pp; 113842354Smckusick register unsigned head; 113942354Smckusick int s, c, stat, port; 114042354Smckusick 114142354Smckusick port = PORT(dev); 114242354Smckusick pp = dcm_preg(dcm, port); 114342354Smckusick s = splhigh(); 114442354Smckusick head = pp->r_head & RX_MASK; 114542354Smckusick fifo = &dcm->dcm_rfifos[3-port][head>>1]; 114642354Smckusick while (head == (pp->r_tail & RX_MASK)) 114742354Smckusick ; 114842354Smckusick /* 114942354Smckusick * If board interrupts are enabled, just let our received char 115042354Smckusick * interrupt through in case some other port on the board was 115142354Smckusick * busy. Otherwise we must clear the interrupt. 115242354Smckusick */ 115342354Smckusick SEM_LOCK(dcm); 115442354Smckusick if ((dcm->dcm_ic & IC_IE) == 0) 115542354Smckusick stat = dcm->dcm_iir; 115642354Smckusick SEM_UNLOCK(dcm); 115742354Smckusick c = fifo->data_char; 115842354Smckusick stat = fifo->data_stat; 115942354Smckusick pp->r_head = (head + 2) & RX_MASK; 116042354Smckusick splx(s); 116142354Smckusick return(c); 116241480Smckusick } 116341480Smckusick 116441480Smckusick /* 116541480Smckusick * Console kernel output character routine. 116641480Smckusick */ 116741480Smckusick dcmcnputc(dev, c) 116841480Smckusick dev_t dev; 116942354Smckusick int c; 117041480Smckusick { 117141480Smckusick register struct dcmdevice *dcm = dcm_addr[BOARD(dev)]; 117242354Smckusick register struct dcmpreg *pp; 117342354Smckusick unsigned tail; 117442354Smckusick int s, port, stat; 117541480Smckusick 117642354Smckusick port = PORT(dev); 117742354Smckusick pp = dcm_preg(dcm, port); 117842354Smckusick s = splhigh(); 117942354Smckusick #ifdef KGDB 118042354Smckusick if (dev != kgdb_dev) 118142354Smckusick #endif 118242354Smckusick if (dcmconsole == -1) { 118342354Smckusick (void) dcminit(dev, dcmdefaultrate); 118442354Smckusick dcmconsole = UNIT(dev); 118542354Smckusick } 118642354Smckusick tail = pp->t_tail & TX_MASK; 118742354Smckusick while (tail != (pp->t_head & TX_MASK)) 118842354Smckusick ; 118942354Smckusick dcm->dcm_tfifos[3-port][tail].data_char = c; 119042354Smckusick pp->t_tail = tail = (tail + 1) & TX_MASK; 119141480Smckusick SEM_LOCK(dcm); 119242354Smckusick dcm->dcm_cmdtab[port].dcm_data |= CT_TX; 119342354Smckusick dcm->dcm_cr |= (1 << port); 119441480Smckusick SEM_UNLOCK(dcm); 119542354Smckusick while (tail != (pp->t_head & TX_MASK)) 119642354Smckusick ; 119742354Smckusick /* 119842354Smckusick * If board interrupts are enabled, just let our completion 119942354Smckusick * interrupt through in case some other port on the board 120042354Smckusick * was busy. Otherwise we must clear the interrupt. 120142354Smckusick */ 120242354Smckusick if ((dcm->dcm_ic & IC_IE) == 0) { 120342354Smckusick SEM_LOCK(dcm); 120442354Smckusick stat = dcm->dcm_iir; 120542354Smckusick SEM_UNLOCK(dcm); 120642354Smckusick } 120741480Smckusick splx(s); 120841480Smckusick } 120941480Smckusick #endif 1210