cy.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,542 行 · 第 1/5 页
C
2,542 行
}static intsioattach(isdp) struct isa_device *isdp;{ int cyu; cy_addr cy_iobase; cy_addr iobase; int ncyu; int unit; unit = isdp->id_unit; if ((u_int)unit >= NCY) return (0); ncyu = cy_nr_cd1400s[unit]; if (ncyu == 0) return (0); isdp->id_ri_flags |= RI_FAST; cy_iobase = (cy_addr)isdp->id_maddr; unit *= CY_MAX_PORTS; for (cyu = 0, iobase = cy_iobase; cyu < ncyu; ++cyu, iobase += CY_CD1400_MEMSIZE) { int cdu; /* Set up a receive timeout period of than 1+ ms. */ cd_outb(iobase, CD1400_PPR, howmany(CY_CLOCK / CD1400_PPR_PRESCALER, 1000)); for (cdu = 0; cdu < CD1400_NO_OF_CHANNELS; ++cdu, ++unit) { struct com_s *com; int s; com = malloc(sizeof *com, M_DEVBUF, M_NOWAIT); if (com == NULL) break; bzero(com, sizeof *com); com->unit = unit; com->dtr_wait = 3 * hz; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; com->ihighwater = com->ibuf1 + RS_IHIGHWATER; com->obufs[0].l_head = com->obuf1; com->obufs[1].l_head = com->obuf2; com->cy_iobase = cy_iobase; com->iobase = iobase; /* * We don't use all the flags from <sys/ttydefaults.h> since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ com->it_in.c_iflag = 0; com->it_in.c_oflag = 0; com->it_in.c_cflag = TTYDEF_CFLAG; com->it_in.c_lflag = 0; if (unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)) { com->it_in.c_iflag = TTYDEF_IFLAG; com->it_in.c_oflag = TTYDEF_OFLAG; com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; com->it_in.c_lflag = TTYDEF_LFLAG; com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; } termioschars(&com->it_in); com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; com->it_out = com->it_in; com->kdc = kdc_sio[0]; com->kdc.kdc_name = "cy"; com->kdc.kdc_unit = unit; com->kdc.kdc_isa = isdp; com->kdc.kdc_parent = &kdc_sio[isdp->id_unit]; com->kdc.kdc_state = DC_IDLE; com->kdc.kdc_description = "Serial port: Cirrus Logic CD1400"; com->kdc.kdc_class = DC_CLS_SERIAL; dev_attach(&com->kdc); s = spltty(); com_addr(unit) = com; register_swi(SWI_TTY, siopoll); splx(s); } } kdc_sio[isdp->id_unit].kdc_state = DC_BUSY; /* XXX */ /* ensure an edge for the next interrupt */ cy_outb(cy_iobase, CY_CLEAR_INTR, 0); return (1);}intsioopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p;{ struct com_s *com; int error; cy_addr iobase; int mynor; int s; struct tty *tp; int unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) return (ENXIO); if (mynor & CONTROL_MASK) return (0);#if 0 /* XXX */ tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]);#else tp = com->tp = &sio_tty[unit];#endif s = spltty(); /* * We jump to this label after all non-interrupted sleeps to pick * up any changes of the device state. */open_top: while (com->state & CS_DTR_OFF) { error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "cydtr", 0); if (error != 0) goto out; } com->kdc.kdc_state = DC_BUSY; if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!com->active_out) { error = EBUSY; goto out; } } else { if (com->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&com->active_out, TTIPRI | PCATCH, "cybi", 0); if (error != 0) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc = comstart; tp->t_param = comparam; tp->t_dev = dev; tp->t_termios = mynor & CALLOUT_MASK ? com->it_out : com->it_in;#if 0 (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); com->ftl_max = com->ftl_init; com->poll = com->no_irq; com->poll_output = com->loses_outints;#endif ++com->wopeners; iobase = com->iobase; /* reset this channel */ cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN); cd1400_channel_cmd(iobase, CD1400_CCR_CMDRESET); /* * Resetting disables the transmitter and receiver as well as * flushing the fifos so some of our cached state becomes * invalid. The documentation suggests that all registers * for the current channel are reset to defaults, but * apparently none are. We wouldn't want DTR cleared. */ com->channel_control = 0; /* Encode per-board unit in LIVR for access in intr routines. */ cd_outb(iobase, CD1400_LIVR, (unit & CD1400_xIVR_CHAN) << CD1400_xIVR_CHAN_SHIFT); /* * raise dtr and generally set things up correctly. this * has the side-effect of selecting the appropriate cd1400 * channel, to help us with subsequent channel control stuff */ error = comparam(tp, &tp->t_termios); --com->wopeners; if (error != 0) goto out; /* * XXX we should goto open_top if comparam() slept. */ ttsetwater(tp);#if 0 if (com->hasfifo) { /* * (Re)enable and drain fifos. * * Certain SMC chips cause problems if the fifos * are enabled while input is ready. Turn off the * fifo if necessary to clear the input. We test * the input ready bit after enabling the fifos * since we've already enabled them in comparam() * and to handle races between enabling and fresh * input. */ while (TRUE) { outb(iobase + com_fifo, FIFO_RCV_RST | FIFO_XMT_RST | FIFO_ENABLE | com->ftl); DELAY(100); if (!(inb(com->line_status_port) & LSR_RXRDY)) break; outb(iobase + com_fifo, 0); DELAY(100); (void) inb(com->data_port); } } disable_intr(); (void) inb(com->line_status_port); (void) inb(com->data_port); com->prev_modem_status = com->last_modem_status = inb(com->modem_status_port); outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); enable_intr();#else /* !0 */ /* XXX raise RTS too */ (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); disable_intr(); com->prev_modem_status = com->last_modem_status = cd_inb(iobase, CD1400_MSVR2); cd_outb(iobase, CD1400_SRER, com->intr_enable = CD1400_SRER_MDMCH | CD1400_SRER_RXDATA); enable_intr();#endif /* 0 */ /* * Handle initial DCD. Callout devices get a fake initial * DCD (trapdoor DCD). If we are callout, then any sleeping * callin opens get woken up and resume sleeping on "cybi" * instead of "cydcd". */ /* * XXX `mynor & CALLOUT_MASK' should be * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where * TRAPDOOR_CARRIER is the default initial state for callout * devices and SOFT_CARRIER is like CLOCAL except it hides * the true carrier. */ if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) (*linesw[tp->t_line].l_modem)(tp, 1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++com->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "cydcd", 0); --com->wopeners; if (error != 0) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); disc_optim(tp, &tp->t_termios, com); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) com->active_out = TRUE; siosettimeout();out: splx(s); if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) comhardclose(com); return (error);}intsioclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p;{ struct com_s *com; int mynor; int s; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (0); com = com_addr(MINOR_TO_UNIT(mynor)); tp = com->tp; s = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); disc_optim(tp, &tp->t_termios, com); siostop(tp, FREAD | FWRITE); comhardclose(com); ttyclose(tp); siosettimeout(); splx(s);#ifdef broken /* session holds a ref to the tty; can't deallocate */ ttyfree(tp); com->tp = sio_tty[unit] = NULL;#endif return (0);}static voidcomhardclose(com) struct com_s *com;{ cy_addr iobase; int s; struct tty *tp; int unit; unit = com->unit; iobase = com->iobase; s = spltty();#if 0 com->poll = FALSE; com->poll_output = FALSE;#endif com->do_timestamp = 0; cd_outb(iobase, CD1400_CAR, unit & CD1400_CAR_CHAN);#if 0 outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);#endif#ifdef KGDB /* do not disable interrupts or hang up if debugging */ if (kgdb_dev != makedev(commajor, unit))#endif {#if 0 outb(iobase + com_ier, 0);#else disable_intr(); cd_outb(iobase, CD1400_SRER, com->intr_enable = 0); enable_intr();#endif tp = com->tp; if (tp->t_cflag & HUPCL /* * XXX we will miss any carrier drop between here and the * next open. Perhaps we should watch DCD even when the * port is closed; it is not sufficient to check it at * the next open because it might go up and down while * we're not watching. */ || !com->active_out && !(com->prev_modem_status & MSR_DCD) && !(com->it_in.c_cflag & CLOCAL) || !(tp->t_state & TS_ISOPEN)) { (void)commctl(com, TIOCM_DTR, DMBIC); /* Disable receiver (leave transmitter enabled). */ com->channel_control = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN | CD1400_CCR_RCVDIS; cd1400_channel_cmd(iobase, com->channel_control); if (com->dtr_wait != 0) { timeout(siodtrwakeup, com, com->dtr_wait); com->state |= CS_DTR_OFF; } } } com->active_out = FALSE; wakeup(&com->active_out); wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ if (!(com->state & CS_DTR_OFF) && !(unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL))) com->kdc.kdc_state = DC_IDLE; splx(s);}intsioread(dev, uio, flag) dev_t dev; struct uio *uio; int flag;{ int mynor; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); tp = com_addr(MINOR_TO_UNIT(mynor))->tp; return ((*linesw[tp->t_line].l_read)(tp, uio, flag));}intsiowrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag;{ int mynor; struct tty *tp; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); tp = com_addr(unit)->tp; /* * (XXX) We disallow virtual consoles if the physical console is * a serial port. This is in case there is a display attached that * is not the console. In that situation we don't need/want the X * server taking over the console. */ if (constty && unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL)) constty = NULL;#ifdef Smarts /* XXX duplicate ttwrite(), but without so much output processing on * CR & LF chars. Hardly worth the effort, given that high-throughput * sessions are raw anyhow. */#else return ((*linesw[tp->t_line].l_write)(tp, uio, flag));#endif}static voidsiodtrwakeup(chan) void *chan;{ struct com_s *com; com = (struct com_s *)chan; com->state &= ~CS_DTR_OFF; if (!(com->unit == comconsole && (COMCONSOLE || boothowto & RB_SERIAL))) com->kdc.kdc_state = DC_IDLE; wakeup(&com->dtr_wait);}/* Interrupt routine for timekeeping purposes */voidsiointrts(unit) int unit;{#ifdef OSKIT microtime(&intr_timestamp);#else /* * XXX microtime() reenables CPU interrupts. We can't afford to * be interrupted and don't want to slow down microtime(), so lock * out interrupts in another way. */ outb(IO_ICU1 + 1, 0xff); microtime(&intr_timestamp); disable_intr(); outb(IO_ICU1 + 1, imen);#endif siointr(unit);}voidsiointr(unit) int unit;{ int baseu; cy_addr cy_iobase; int cyu; cy_addr iobase; u_char status; baseu = unit * CY_MAX_PORTS; cy_iobase = com_addr(baseu)->cy_iobase; /* check each CD1400 in turn */ for (cyu = 0, iobase = cy_iobase; cyu < cy_nr_cd1400s[unit]; ++cyu, iobase += CY_CD1400_MEMSIZE) { /* poll to see if it has any work */ status = cd_inb(iobase, CD1400_SVRR); if (status == 0) continue;#ifdef CyDebug ++cy_svrr_probes;#endif /* service requests as appropriate, giving priority to RX */ if (status & CD1400_SVRR_RXRDY) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?