786727e51f
THRE interrupt occurs between the LSR read and IIR read, we won't see the LSR_TXRDY bit when testing it in the variable `lsr' and we don't interrupted again (as the corresponding bit in the IIR is cleared by reading, except for some broken device). Tested by Matthias Scheler and me, reviewed by Allen Briggs. Closes PR#25010.
2558 lines
58 KiB
C
2558 lines
58 KiB
C
/* $NetBSD: com.c,v 1.225 2004/04/05 22:33:08 enami Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 1998, 1999 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Charles M. Hannum.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the NetBSD
|
|
* Foundation, Inc. and its contributors.
|
|
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 1991 The Regents of the University of California.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* @(#)com.c 7.5 (Berkeley) 5/16/91
|
|
*/
|
|
|
|
/*
|
|
* COM driver, uses National Semiconductor NS16450/NS16550AF UART
|
|
* Supports automatic hardware flow control on StarTech ST16C650A UART
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: com.c,v 1.225 2004/04/05 22:33:08 enami Exp $");
|
|
|
|
#include "opt_com.h"
|
|
#include "opt_ddb.h"
|
|
#include "opt_kgdb.h"
|
|
#include "opt_lockdebug.h"
|
|
#include "opt_multiprocessor.h"
|
|
#include "opt_ntp.h"
|
|
|
|
#include "rnd.h"
|
|
#if NRND > 0 && defined(RND_COM)
|
|
#include <sys/rnd.h>
|
|
#endif
|
|
|
|
/*
|
|
* Override cnmagic(9) macro before including <sys/systm.h>.
|
|
* We need to know if cn_check_magic triggered debugger, so set a flag.
|
|
* Callers of cn_check_magic must declare int cn_trapped = 0;
|
|
* XXX: this is *ugly*!
|
|
*/
|
|
#define cn_trap() \
|
|
do { \
|
|
console_debugger(); \
|
|
cn_trapped = 1; \
|
|
} while (/* CONSTCOND */ 0)
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/select.h>
|
|
#include <sys/tty.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/user.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/file.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/device.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/timepps.h>
|
|
#include <sys/vnode.h>
|
|
|
|
#include <machine/intr.h>
|
|
#include <machine/bus.h>
|
|
|
|
#include <dev/ic/comreg.h>
|
|
#include <dev/ic/comvar.h>
|
|
#include <dev/ic/ns16550reg.h>
|
|
#include <dev/ic/st16650reg.h>
|
|
#ifdef COM_HAYESP
|
|
#include <dev/ic/hayespreg.h>
|
|
#endif
|
|
#define com_lcr com_cfcr
|
|
#include <dev/cons.h>
|
|
|
|
#ifdef COM_HAYESP
|
|
int comprobeHAYESP(bus_space_handle_t hayespioh, struct com_softc *sc);
|
|
#endif
|
|
|
|
static void com_enable_debugport(struct com_softc *);
|
|
|
|
void com_config(struct com_softc *);
|
|
void com_shutdown(struct com_softc *);
|
|
int comspeed(long, long, int);
|
|
static u_char cflag2lcr(tcflag_t);
|
|
int comparam(struct tty *, struct termios *);
|
|
void comstart(struct tty *);
|
|
int comhwiflow(struct tty *, int);
|
|
|
|
void com_loadchannelregs(struct com_softc *);
|
|
void com_hwiflow(struct com_softc *);
|
|
void com_break(struct com_softc *, int);
|
|
void com_modem(struct com_softc *, int);
|
|
void tiocm_to_com(struct com_softc *, u_long, int);
|
|
int com_to_tiocm(struct com_softc *);
|
|
void com_iflush(struct com_softc *);
|
|
|
|
int com_common_getc(dev_t, bus_space_tag_t, bus_space_handle_t);
|
|
void com_common_putc(dev_t, bus_space_tag_t, bus_space_handle_t, int);
|
|
|
|
int cominit(bus_space_tag_t, bus_addr_t, int, int, int, tcflag_t,
|
|
bus_space_handle_t *);
|
|
|
|
int comcngetc(dev_t);
|
|
void comcnputc(dev_t, int);
|
|
void comcnpollc(dev_t, int);
|
|
|
|
#define integrate static inline
|
|
#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
|
|
void comsoft(void *);
|
|
#else
|
|
#ifndef __NO_SOFT_SERIAL_INTERRUPT
|
|
void comsoft(void);
|
|
#else
|
|
void comsoft(void *);
|
|
static struct callout comsoft_callout = CALLOUT_INITIALIZER;
|
|
#endif
|
|
#endif
|
|
integrate void com_rxsoft(struct com_softc *, struct tty *);
|
|
integrate void com_txsoft(struct com_softc *, struct tty *);
|
|
integrate void com_stsoft(struct com_softc *, struct tty *);
|
|
integrate void com_schedrx(struct com_softc *);
|
|
void comdiag(void *);
|
|
|
|
extern struct cfdriver com_cd;
|
|
|
|
dev_type_open(comopen);
|
|
dev_type_close(comclose);
|
|
dev_type_read(comread);
|
|
dev_type_write(comwrite);
|
|
dev_type_ioctl(comioctl);
|
|
dev_type_stop(comstop);
|
|
dev_type_tty(comtty);
|
|
dev_type_poll(compoll);
|
|
|
|
const struct cdevsw com_cdevsw = {
|
|
comopen, comclose, comread, comwrite, comioctl,
|
|
comstop, comtty, compoll, nommap, ttykqfilter, D_TTY
|
|
};
|
|
|
|
/*
|
|
* Make this an option variable one can patch.
|
|
* But be warned: this must be a power of 2!
|
|
*/
|
|
u_int com_rbuf_size = COM_RING_SIZE;
|
|
|
|
/* Stop input when 3/4 of the ring is full; restart when only 1/4 is full. */
|
|
u_int com_rbuf_hiwat = (COM_RING_SIZE * 1) / 4;
|
|
u_int com_rbuf_lowat = (COM_RING_SIZE * 3) / 4;
|
|
|
|
static bus_addr_t comconsaddr;
|
|
static bus_space_tag_t comconstag;
|
|
static bus_space_handle_t comconsioh;
|
|
static int comconsattached;
|
|
static int comconsrate;
|
|
static tcflag_t comconscflag;
|
|
static struct cnm_state com_cnm_state;
|
|
|
|
static int ppscap =
|
|
PPS_TSFMT_TSPEC |
|
|
PPS_CAPTUREASSERT |
|
|
PPS_CAPTURECLEAR |
|
|
PPS_OFFSETASSERT | PPS_OFFSETCLEAR;
|
|
|
|
#ifndef __HAVE_GENERIC_SOFT_INTERRUPTS
|
|
#ifdef __NO_SOFT_SERIAL_INTERRUPT
|
|
volatile int com_softintr_scheduled;
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef KGDB
|
|
#include <sys/kgdb.h>
|
|
|
|
static bus_addr_t com_kgdb_addr;
|
|
static bus_space_tag_t com_kgdb_iot;
|
|
static bus_space_handle_t com_kgdb_ioh;
|
|
static int com_kgdb_attached;
|
|
|
|
int com_kgdb_getc(void *);
|
|
void com_kgdb_putc(void *, int);
|
|
#endif /* KGDB */
|
|
|
|
#define COMUNIT_MASK 0x7ffff
|
|
#define COMDIALOUT_MASK 0x80000
|
|
|
|
#define COMUNIT(x) (minor(x) & COMUNIT_MASK)
|
|
#define COMDIALOUT(x) (minor(x) & COMDIALOUT_MASK)
|
|
|
|
#define COM_ISALIVE(sc) ((sc)->enabled != 0 && \
|
|
ISSET((sc)->sc_dev.dv_flags, DVF_ACTIVE))
|
|
|
|
#define BR BUS_SPACE_BARRIER_READ
|
|
#define BW BUS_SPACE_BARRIER_WRITE
|
|
#define COM_BARRIER(t, h, f) bus_space_barrier((t), (h), 0, COM_NPORTS, (f))
|
|
|
|
#if (defined(MULTIPROCESSOR) || defined(LOCKDEBUG)) && defined(COM_MPLOCK)
|
|
|
|
#define COM_LOCK(sc) simple_lock(&(sc)->sc_lock)
|
|
#define COM_UNLOCK(sc) simple_unlock(&(sc)->sc_lock)
|
|
|
|
#else
|
|
|
|
#define COM_LOCK(sc)
|
|
#define COM_UNLOCK(sc)
|
|
|
|
#endif
|
|
|
|
/*ARGSUSED*/
|
|
int
|
|
comspeed(long speed, long frequency, int type)
|
|
{
|
|
#define divrnd(n, q) (((n)*2/(q)+1)/2) /* divide and round off */
|
|
|
|
int x, err;
|
|
|
|
#if 0
|
|
if (speed == 0)
|
|
return (0);
|
|
#endif
|
|
if (speed <= 0)
|
|
return (-1);
|
|
x = divrnd(frequency / 16, speed);
|
|
if (x <= 0)
|
|
return (-1);
|
|
err = divrnd(((quad_t)frequency) * 1000 / 16, speed * x) - 1000;
|
|
if (err < 0)
|
|
err = -err;
|
|
if (err > COM_TOLERANCE)
|
|
return (-1);
|
|
return (x);
|
|
|
|
#undef divrnd
|
|
}
|
|
|
|
#ifdef COM_DEBUG
|
|
int com_debug = 0;
|
|
|
|
void comstatus(struct com_softc *, char *);
|
|
void
|
|
comstatus(struct com_softc *sc, char *str)
|
|
{
|
|
struct tty *tp = sc->sc_tty;
|
|
|
|
printf("%s: %s %cclocal %cdcd %cts_carr_on %cdtr %ctx_stopped\n",
|
|
sc->sc_dev.dv_xname, str,
|
|
ISSET(tp->t_cflag, CLOCAL) ? '+' : '-',
|
|
ISSET(sc->sc_msr, MSR_DCD) ? '+' : '-',
|
|
ISSET(tp->t_state, TS_CARR_ON) ? '+' : '-',
|
|
ISSET(sc->sc_mcr, MCR_DTR) ? '+' : '-',
|
|
sc->sc_tx_stopped ? '+' : '-');
|
|
|
|
printf("%s: %s %ccrtscts %ccts %cts_ttstop %crts rx_flags=0x%x\n",
|
|
sc->sc_dev.dv_xname, str,
|
|
ISSET(tp->t_cflag, CRTSCTS) ? '+' : '-',
|
|
ISSET(sc->sc_msr, MSR_CTS) ? '+' : '-',
|
|
ISSET(tp->t_state, TS_TTSTOP) ? '+' : '-',
|
|
ISSET(sc->sc_mcr, MCR_RTS) ? '+' : '-',
|
|
sc->sc_rx_flags);
|
|
}
|
|
#endif
|
|
|
|
int
|
|
comprobe1(bus_space_tag_t iot, bus_space_handle_t ioh)
|
|
{
|
|
|
|
/* force access to id reg */
|
|
bus_space_write_1(iot, ioh, com_lcr, LCR_8BITS);
|
|
bus_space_write_1(iot, ioh, com_iir, 0);
|
|
if ((bus_space_read_1(iot, ioh, com_lcr) != LCR_8BITS) ||
|
|
(bus_space_read_1(iot, ioh, com_iir) & 0x38))
|
|
return (0);
|
|
|
|
return (1);
|
|
}
|
|
|
|
#ifdef COM_HAYESP
|
|
int
|
|
comprobeHAYESP(bus_space_handle_t hayespioh, struct com_softc *sc)
|
|
{
|
|
char val, dips;
|
|
int combaselist[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
/*
|
|
* Hayes ESP cards have two iobases. One is for compatibility with
|
|
* 16550 serial chips, and at the same ISA PC base addresses. The
|
|
* other is for ESP-specific enhanced features, and lies at a
|
|
* different addressing range entirely (0x140, 0x180, 0x280, or 0x300).
|
|
*/
|
|
|
|
/* Test for ESP signature */
|
|
if ((bus_space_read_1(iot, hayespioh, 0) & 0xf3) == 0)
|
|
return (0);
|
|
|
|
/*
|
|
* ESP is present at ESP enhanced base address; unknown com port
|
|
*/
|
|
|
|
/* Get the dip-switch configurations */
|
|
bus_space_write_1(iot, hayespioh, HAYESP_CMD1, HAYESP_GETDIPS);
|
|
dips = bus_space_read_1(iot, hayespioh, HAYESP_STATUS1);
|
|
|
|
/* Determine which com port this ESP card services: bits 0,1 of */
|
|
/* dips is the port # (0-3); combaselist[val] is the com_iobase */
|
|
if (sc->sc_iobase != combaselist[dips & 0x03])
|
|
return (0);
|
|
|
|
printf(": ESP");
|
|
|
|
/* Check ESP Self Test bits. */
|
|
/* Check for ESP version 2.0: bits 4,5,6 == 010 */
|
|
bus_space_write_1(iot, hayespioh, HAYESP_CMD1, HAYESP_GETTEST);
|
|
val = bus_space_read_1(iot, hayespioh, HAYESP_STATUS1); /* Clear reg1 */
|
|
val = bus_space_read_1(iot, hayespioh, HAYESP_STATUS2);
|
|
if ((val & 0x70) < 0x20) {
|
|
printf("-old (%o)", val & 0x70);
|
|
/* we do not support the necessary features */
|
|
return (0);
|
|
}
|
|
|
|
/* Check for ability to emulate 16550: bit 8 == 1 */
|
|
if ((dips & 0x80) == 0) {
|
|
printf(" slave");
|
|
/* XXX Does slave really mean no 16550 support?? */
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* If we made it this far, we are a full-featured ESP v2.0 (or
|
|
* better), at the correct com port address.
|
|
*/
|
|
|
|
sc->sc_type = COM_TYPE_HAYESP;
|
|
printf(", 1024 byte fifo\n");
|
|
return (1);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
com_enable_debugport(struct com_softc *sc)
|
|
{
|
|
int s;
|
|
|
|
/* Turn on line break interrupt, set carrier. */
|
|
s = splserial();
|
|
COM_LOCK(sc);
|
|
sc->sc_ier = IER_ERXRDY;
|
|
#ifdef COM_PXA2X0
|
|
if (sc->sc_type == COM_TYPE_PXA2x0)
|
|
sc->sc_ier |= IER_EUART | IER_ERXTOUT;
|
|
#endif
|
|
bus_space_write_1(sc->sc_iot, sc->sc_ioh, com_ier, sc->sc_ier);
|
|
SET(sc->sc_mcr, MCR_DTR | MCR_RTS);
|
|
bus_space_write_1(sc->sc_iot, sc->sc_ioh, com_mcr, sc->sc_mcr);
|
|
COM_UNLOCK(sc);
|
|
splx(s);
|
|
}
|
|
|
|
void
|
|
com_attach_subr(struct com_softc *sc)
|
|
{
|
|
bus_addr_t iobase = sc->sc_iobase;
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
struct tty *tp;
|
|
#ifdef COM16650
|
|
u_int8_t lcr;
|
|
#endif
|
|
#ifdef COM_HAYESP
|
|
int hayesp_ports[] = { 0x140, 0x180, 0x280, 0x300, 0 };
|
|
int *hayespp;
|
|
#endif
|
|
const char *fifo_msg = NULL;
|
|
|
|
callout_init(&sc->sc_diag_callout);
|
|
#if (defined(MULTIPROCESSOR) || defined(LOCKDEBUG)) && defined(COM_MPLOCK)
|
|
simple_lock_init(&sc->sc_lock);
|
|
#endif
|
|
|
|
/* Disable interrupts before configuring the device. */
|
|
#ifdef COM_PXA2X0
|
|
if (sc->sc_type == COM_TYPE_PXA2x0)
|
|
sc->sc_ier = IER_EUART;
|
|
else
|
|
#endif
|
|
sc->sc_ier = 0;
|
|
bus_space_write_1(iot, ioh, com_ier, sc->sc_ier);
|
|
|
|
if (iot == comconstag && iobase == comconsaddr) {
|
|
comconsattached = 1;
|
|
|
|
/* Make sure the console is always "hardwired". */
|
|
delay(1000); /* wait for output to finish */
|
|
SET(sc->sc_hwflags, COM_HW_CONSOLE);
|
|
SET(sc->sc_swflags, TIOCFLAG_SOFTCAR);
|
|
}
|
|
|
|
#ifdef COM_HAYESP
|
|
sc->sc_prescaler = 0; /* set prescaler to x1. */
|
|
|
|
/* Look for a Hayes ESP board. */
|
|
for (hayespp = hayesp_ports; *hayespp != 0; hayespp++) {
|
|
bus_space_handle_t hayespioh;
|
|
|
|
#define HAYESP_NPORTS 8 /* XXX XXX XXX ??? ??? ??? */
|
|
if (bus_space_map(iot, *hayespp, HAYESP_NPORTS, 0, &hayespioh))
|
|
continue;
|
|
if (comprobeHAYESP(hayespioh, sc)) {
|
|
sc->sc_hayespioh = hayespioh;
|
|
sc->sc_fifolen = 1024;
|
|
|
|
break;
|
|
}
|
|
bus_space_unmap(iot, hayespioh, HAYESP_NPORTS);
|
|
}
|
|
/* No ESP; look for other things. */
|
|
if (sc->sc_type != COM_TYPE_HAYESP) {
|
|
#endif
|
|
sc->sc_fifolen = 1;
|
|
/* look for a NS 16550AF UART with FIFOs */
|
|
bus_space_write_1(iot, ioh, com_fifo,
|
|
FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_14);
|
|
delay(100);
|
|
if (ISSET(bus_space_read_1(iot, ioh, com_iir), IIR_FIFO_MASK)
|
|
== IIR_FIFO_MASK)
|
|
if (ISSET(bus_space_read_1(iot, ioh, com_fifo), FIFO_TRIGGER_14)
|
|
== FIFO_TRIGGER_14) {
|
|
SET(sc->sc_hwflags, COM_HW_FIFO);
|
|
|
|
#ifdef COM16650
|
|
/*
|
|
* IIR changes into the EFR if LCR is set to LCR_EERS
|
|
* on 16650s. We also know IIR != 0 at this point.
|
|
* Write 0 into the EFR, and read it. If the result
|
|
* is 0, we have a 16650.
|
|
*
|
|
* Older 16650s were broken; the test to detect them
|
|
* is taken from the Linux driver. Apparently
|
|
* setting DLAB enable gives access to the EFR on
|
|
* these chips.
|
|
*/
|
|
lcr = bus_space_read_1(iot, ioh, com_lcr);
|
|
bus_space_write_1(iot, ioh, com_lcr, LCR_EERS);
|
|
bus_space_write_1(iot, ioh, com_efr, 0);
|
|
if (bus_space_read_1(iot, ioh, com_efr) == 0) {
|
|
bus_space_write_1(iot, ioh, com_lcr,
|
|
lcr | LCR_DLAB);
|
|
if (bus_space_read_1(iot, ioh, com_efr) == 0) {
|
|
CLR(sc->sc_hwflags, COM_HW_FIFO);
|
|
sc->sc_fifolen = 0;
|
|
} else {
|
|
SET(sc->sc_hwflags, COM_HW_FLOW);
|
|
sc->sc_fifolen = 32;
|
|
}
|
|
} else
|
|
#endif
|
|
sc->sc_fifolen = 16;
|
|
|
|
#ifdef COM16650
|
|
bus_space_write_1(iot, ioh, com_lcr, lcr);
|
|
if (sc->sc_fifolen == 0)
|
|
fifo_msg = "st16650, broken fifo";
|
|
else if (sc->sc_fifolen == 32)
|
|
fifo_msg = "st16650a, working fifo";
|
|
else
|
|
#endif
|
|
fifo_msg = "ns16550a, working fifo";
|
|
} else
|
|
fifo_msg = "ns16550, broken fifo";
|
|
else
|
|
fifo_msg = "ns8250 or ns16450, no fifo";
|
|
bus_space_write_1(iot, ioh, com_fifo, 0);
|
|
/*
|
|
* Some chips will clear down both Tx and Rx FIFOs when zero is
|
|
* written to com_fifo. If this chip is the console, writing zero
|
|
* results in some of the chip/FIFO description being lost, so delay
|
|
* printing it until now.
|
|
*/
|
|
delay(10);
|
|
aprint_normal(": %s\n", fifo_msg);
|
|
if (ISSET(sc->sc_hwflags, COM_HW_TXFIFO_DISABLE)) {
|
|
sc->sc_fifolen = 1;
|
|
aprint_normal("%s: txfifo disabled\n", sc->sc_dev.dv_xname);
|
|
}
|
|
#ifdef COM_HAYESP
|
|
}
|
|
#endif
|
|
|
|
tp = ttymalloc();
|
|
tp->t_oproc = comstart;
|
|
tp->t_param = comparam;
|
|
tp->t_hwiflow = comhwiflow;
|
|
|
|
sc->sc_tty = tp;
|
|
sc->sc_rbuf = malloc(com_rbuf_size << 1, M_DEVBUF, M_NOWAIT);
|
|
sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
|
|
sc->sc_rbavail = com_rbuf_size;
|
|
if (sc->sc_rbuf == NULL) {
|
|
aprint_error("%s: unable to allocate ring buffer\n",
|
|
sc->sc_dev.dv_xname);
|
|
return;
|
|
}
|
|
sc->sc_ebuf = sc->sc_rbuf + (com_rbuf_size << 1);
|
|
|
|
tty_attach(tp);
|
|
|
|
if (!ISSET(sc->sc_hwflags, COM_HW_NOIEN))
|
|
SET(sc->sc_mcr, MCR_IENABLE);
|
|
|
|
if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
|
|
int maj;
|
|
|
|
/* locate the major number */
|
|
maj = cdevsw_lookup_major(&com_cdevsw);
|
|
|
|
tp->t_dev = cn_tab->cn_dev = makedev(maj, sc->sc_dev.dv_unit);
|
|
|
|
aprint_normal("%s: console\n", sc->sc_dev.dv_xname);
|
|
}
|
|
|
|
#ifdef KGDB
|
|
/*
|
|
* Allow kgdb to "take over" this port. If this is
|
|
* not the console and is the kgdb device, it has
|
|
* exclusive use. If it's the console _and_ the
|
|
* kgdb device, it doesn't.
|
|
*/
|
|
if (iot == com_kgdb_iot && iobase == com_kgdb_addr) {
|
|
if (!ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
|
|
com_kgdb_attached = 1;
|
|
|
|
SET(sc->sc_hwflags, COM_HW_KGDB);
|
|
}
|
|
aprint_normal("%s: kgdb\n", sc->sc_dev.dv_xname);
|
|
}
|
|
#endif
|
|
|
|
#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
|
|
sc->sc_si = softintr_establish(IPL_SOFTSERIAL, comsoft, sc);
|
|
#endif
|
|
|
|
#if NRND > 0 && defined(RND_COM)
|
|
rnd_attach_source(&sc->rnd_source, sc->sc_dev.dv_xname,
|
|
RND_TYPE_TTY, 0);
|
|
#endif
|
|
|
|
/* if there are no enable/disable functions, assume the device
|
|
is always enabled */
|
|
if (!sc->enable)
|
|
sc->enabled = 1;
|
|
|
|
com_config(sc);
|
|
|
|
SET(sc->sc_hwflags, COM_HW_DEV_OK);
|
|
}
|
|
|
|
void
|
|
com_config(struct com_softc *sc)
|
|
{
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
/* Disable interrupts before configuring the device. */
|
|
#ifdef COM_PXA2X0
|
|
if (sc->sc_type == COM_TYPE_PXA2x0)
|
|
sc->sc_ier = IER_EUART;
|
|
else
|
|
#endif
|
|
sc->sc_ier = 0;
|
|
bus_space_write_1(iot, ioh, com_ier, sc->sc_ier);
|
|
|
|
#ifdef COM_HAYESP
|
|
/* Look for a Hayes ESP board. */
|
|
if (sc->sc_type == COM_TYPE_HAYESP) {
|
|
sc->sc_fifolen = 1024;
|
|
|
|
/* Set 16550 compatibility mode */
|
|
bus_space_write_1(iot, sc->sc_hayespioh, HAYESP_CMD1,
|
|
HAYESP_SETMODE);
|
|
bus_space_write_1(iot, sc->sc_hayespioh, HAYESP_CMD2,
|
|
HAYESP_MODE_FIFO|HAYESP_MODE_RTS|
|
|
HAYESP_MODE_SCALE);
|
|
|
|
/* Set RTS/CTS flow control */
|
|
bus_space_write_1(iot, sc->sc_hayespioh, HAYESP_CMD1,
|
|
HAYESP_SETFLOWTYPE);
|
|
bus_space_write_1(iot, sc->sc_hayespioh, HAYESP_CMD2,
|
|
HAYESP_FLOW_RTS);
|
|
bus_space_write_1(iot, sc->sc_hayespioh, HAYESP_CMD2,
|
|
HAYESP_FLOW_CTS);
|
|
|
|
/* Set flow control levels */
|
|
bus_space_write_1(iot, sc->sc_hayespioh, HAYESP_CMD1,
|
|
HAYESP_SETRXFLOW);
|
|
bus_space_write_1(iot, sc->sc_hayespioh, HAYESP_CMD2,
|
|
HAYESP_HIBYTE(HAYESP_RXHIWMARK));
|
|
bus_space_write_1(iot, sc->sc_hayespioh, HAYESP_CMD2,
|
|
HAYESP_LOBYTE(HAYESP_RXHIWMARK));
|
|
bus_space_write_1(iot, sc->sc_hayespioh, HAYESP_CMD2,
|
|
HAYESP_HIBYTE(HAYESP_RXLOWMARK));
|
|
bus_space_write_1(iot, sc->sc_hayespioh, HAYESP_CMD2,
|
|
HAYESP_LOBYTE(HAYESP_RXLOWMARK));
|
|
}
|
|
#endif
|
|
|
|
if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE|COM_HW_KGDB))
|
|
com_enable_debugport(sc);
|
|
}
|
|
|
|
int
|
|
com_detach(struct device *self, int flags)
|
|
{
|
|
struct com_softc *sc = (struct com_softc *)self;
|
|
int maj, mn;
|
|
|
|
/* locate the major number */
|
|
maj = cdevsw_lookup_major(&com_cdevsw);
|
|
|
|
/* Nuke the vnodes for any open instances. */
|
|
mn = self->dv_unit;
|
|
vdevgone(maj, mn, mn, VCHR);
|
|
|
|
mn |= COMDIALOUT_MASK;
|
|
vdevgone(maj, mn, mn, VCHR);
|
|
|
|
if (sc->sc_rbuf == NULL) {
|
|
/*
|
|
* Ring buffer allocation failed in the com_attach_subr,
|
|
* only the tty is allocated, and nothing else.
|
|
*/
|
|
ttyfree(sc->sc_tty);
|
|
return 0;
|
|
}
|
|
|
|
/* Free the receive buffer. */
|
|
free(sc->sc_rbuf, M_DEVBUF);
|
|
|
|
/* Detach and free the tty. */
|
|
tty_detach(sc->sc_tty);
|
|
ttyfree(sc->sc_tty);
|
|
|
|
#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
|
|
/* Unhook the soft interrupt handler. */
|
|
softintr_disestablish(sc->sc_si);
|
|
#endif
|
|
|
|
#if NRND > 0 && defined(RND_COM)
|
|
/* Unhook the entropy source. */
|
|
rnd_detach_source(&sc->rnd_source);
|
|
#endif
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
com_activate(struct device *self, enum devact act)
|
|
{
|
|
struct com_softc *sc = (struct com_softc *)self;
|
|
int s, rv = 0;
|
|
|
|
s = splserial();
|
|
COM_LOCK(sc);
|
|
switch (act) {
|
|
case DVACT_ACTIVATE:
|
|
rv = EOPNOTSUPP;
|
|
break;
|
|
|
|
case DVACT_DEACTIVATE:
|
|
if (sc->sc_hwflags & (COM_HW_CONSOLE|COM_HW_KGDB)) {
|
|
rv = EBUSY;
|
|
break;
|
|
}
|
|
|
|
if (sc->disable != NULL && sc->enabled != 0) {
|
|
(*sc->disable)(sc);
|
|
sc->enabled = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
COM_UNLOCK(sc);
|
|
splx(s);
|
|
return (rv);
|
|
}
|
|
|
|
void
|
|
com_shutdown(struct com_softc *sc)
|
|
{
|
|
struct tty *tp = sc->sc_tty;
|
|
int s;
|
|
|
|
s = splserial();
|
|
COM_LOCK(sc);
|
|
|
|
/* If we were asserting flow control, then deassert it. */
|
|
SET(sc->sc_rx_flags, RX_IBUF_BLOCKED);
|
|
com_hwiflow(sc);
|
|
|
|
/* Clear any break condition set with TIOCSBRK. */
|
|
com_break(sc, 0);
|
|
|
|
/* Turn off PPS capture on last close. */
|
|
sc->sc_ppsmask = 0;
|
|
sc->ppsparam.mode = 0;
|
|
|
|
/*
|
|
* Hang up if necessary. Wait a bit, so the other side has time to
|
|
* notice even if we immediately open the port again.
|
|
* Avoid tsleeping above splhigh().
|
|
*/
|
|
if (ISSET(tp->t_cflag, HUPCL)) {
|
|
com_modem(sc, 0);
|
|
COM_UNLOCK(sc);
|
|
splx(s);
|
|
/* XXX tsleep will only timeout */
|
|
(void) tsleep(sc, TTIPRI, ttclos, hz);
|
|
s = splserial();
|
|
COM_LOCK(sc);
|
|
}
|
|
|
|
/* Turn off interrupts. */
|
|
if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
|
|
sc->sc_ier = IER_ERXRDY; /* interrupt on break */
|
|
#ifdef COM_PXA2X0
|
|
if (sc->sc_type == COM_TYPE_PXA2x0)
|
|
sc->sc_ier |= IER_ERXTOUT;
|
|
#endif
|
|
} else
|
|
sc->sc_ier = 0;
|
|
|
|
#ifdef COM_PXA2X0
|
|
if (sc->sc_type == COM_TYPE_PXA2x0)
|
|
sc->sc_ier |= IER_EUART;
|
|
#endif
|
|
|
|
bus_space_write_1(sc->sc_iot, sc->sc_ioh, com_ier, sc->sc_ier);
|
|
|
|
if (sc->disable) {
|
|
#ifdef DIAGNOSTIC
|
|
if (!sc->enabled)
|
|
panic("com_shutdown: not enabled?");
|
|
#endif
|
|
(*sc->disable)(sc);
|
|
sc->enabled = 0;
|
|
}
|
|
COM_UNLOCK(sc);
|
|
splx(s);
|
|
}
|
|
|
|
int
|
|
comopen(dev_t dev, int flag, int mode, struct proc *p)
|
|
{
|
|
struct com_softc *sc;
|
|
struct tty *tp;
|
|
int s, s2;
|
|
int error;
|
|
|
|
sc = device_lookup(&com_cd, COMUNIT(dev));
|
|
if (sc == NULL || !ISSET(sc->sc_hwflags, COM_HW_DEV_OK) ||
|
|
sc->sc_rbuf == NULL)
|
|
return (ENXIO);
|
|
|
|
if (ISSET(sc->sc_dev.dv_flags, DVF_ACTIVE) == 0)
|
|
return (ENXIO);
|
|
|
|
#ifdef KGDB
|
|
/*
|
|
* If this is the kgdb port, no other use is permitted.
|
|
*/
|
|
if (ISSET(sc->sc_hwflags, COM_HW_KGDB))
|
|
return (EBUSY);
|
|
#endif
|
|
|
|
tp = sc->sc_tty;
|
|
|
|
if (ISSET(tp->t_state, TS_ISOPEN) &&
|
|
ISSET(tp->t_state, TS_XCLUDE) &&
|
|
p->p_ucred->cr_uid != 0)
|
|
return (EBUSY);
|
|
|
|
s = spltty();
|
|
|
|
/*
|
|
* Do the following iff this is a first open.
|
|
*/
|
|
if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
|
|
struct termios t;
|
|
|
|
tp->t_dev = dev;
|
|
|
|
s2 = splserial();
|
|
COM_LOCK(sc);
|
|
|
|
if (sc->enable) {
|
|
if ((*sc->enable)(sc)) {
|
|
COM_UNLOCK(sc);
|
|
splx(s2);
|
|
splx(s);
|
|
printf("%s: device enable failed\n",
|
|
sc->sc_dev.dv_xname);
|
|
return (EIO);
|
|
}
|
|
sc->enabled = 1;
|
|
com_config(sc);
|
|
}
|
|
|
|
/* Turn on interrupts. */
|
|
sc->sc_ier = IER_ERXRDY | IER_ERLS | IER_EMSC;
|
|
#ifdef COM_PXA2X0
|
|
if (sc->sc_type == COM_TYPE_PXA2x0)
|
|
sc->sc_ier |= IER_EUART | IER_ERXTOUT;
|
|
#endif
|
|
bus_space_write_1(sc->sc_iot, sc->sc_ioh, com_ier, sc->sc_ier);
|
|
|
|
/* Fetch the current modem control status, needed later. */
|
|
sc->sc_msr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, com_msr);
|
|
|
|
/* Clear PPS capture state on first open. */
|
|
sc->sc_ppsmask = 0;
|
|
sc->ppsparam.mode = 0;
|
|
|
|
COM_UNLOCK(sc);
|
|
splx(s2);
|
|
|
|
/*
|
|
* Initialize the termios status to the defaults. Add in the
|
|
* sticky bits from TIOCSFLAGS.
|
|
*/
|
|
t.c_ispeed = 0;
|
|
if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
|
|
t.c_ospeed = comconsrate;
|
|
t.c_cflag = comconscflag;
|
|
} else {
|
|
t.c_ospeed = TTYDEF_SPEED;
|
|
t.c_cflag = TTYDEF_CFLAG;
|
|
}
|
|
if (ISSET(sc->sc_swflags, TIOCFLAG_CLOCAL))
|
|
SET(t.c_cflag, CLOCAL);
|
|
if (ISSET(sc->sc_swflags, TIOCFLAG_CRTSCTS))
|
|
SET(t.c_cflag, CRTSCTS);
|
|
if (ISSET(sc->sc_swflags, TIOCFLAG_MDMBUF))
|
|
SET(t.c_cflag, MDMBUF);
|
|
/* Make sure comparam() will do something. */
|
|
tp->t_ospeed = 0;
|
|
(void) comparam(tp, &t);
|
|
tp->t_iflag = TTYDEF_IFLAG;
|
|
tp->t_oflag = TTYDEF_OFLAG;
|
|
tp->t_lflag = TTYDEF_LFLAG;
|
|
ttychars(tp);
|
|
ttsetwater(tp);
|
|
|
|
s2 = splserial();
|
|
COM_LOCK(sc);
|
|
|
|
/*
|
|
* Turn on DTR. We must always do this, even if carrier is not
|
|
* present, because otherwise we'd have to use TIOCSDTR
|
|
* immediately after setting CLOCAL, which applications do not
|
|
* expect. We always assert DTR while the device is open
|
|
* unless explicitly requested to deassert it.
|
|
*/
|
|
com_modem(sc, 1);
|
|
|
|
/* Clear the input ring, and unblock. */
|
|
sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
|
|
sc->sc_rbavail = com_rbuf_size;
|
|
com_iflush(sc);
|
|
CLR(sc->sc_rx_flags, RX_ANY_BLOCK);
|
|
com_hwiflow(sc);
|
|
|
|
#ifdef COM_DEBUG
|
|
if (com_debug)
|
|
comstatus(sc, "comopen ");
|
|
#endif
|
|
|
|
COM_UNLOCK(sc);
|
|
splx(s2);
|
|
}
|
|
|
|
splx(s);
|
|
|
|
error = ttyopen(tp, COMDIALOUT(dev), ISSET(flag, O_NONBLOCK));
|
|
if (error)
|
|
goto bad;
|
|
|
|
error = (*tp->t_linesw->l_open)(dev, tp);
|
|
if (error)
|
|
goto bad;
|
|
|
|
return (0);
|
|
|
|
bad:
|
|
if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
|
|
/*
|
|
* We failed to open the device, and nobody else had it opened.
|
|
* Clean up the state as appropriate.
|
|
*/
|
|
com_shutdown(sc);
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
comclose(dev_t dev, int flag, int mode, struct proc *p)
|
|
{
|
|
struct com_softc *sc = device_lookup(&com_cd, COMUNIT(dev));
|
|
struct tty *tp = sc->sc_tty;
|
|
|
|
/* XXX This is for cons.c. */
|
|
if (!ISSET(tp->t_state, TS_ISOPEN))
|
|
return (0);
|
|
|
|
(*tp->t_linesw->l_close)(tp, flag);
|
|
ttyclose(tp);
|
|
|
|
if (COM_ISALIVE(sc) == 0)
|
|
return (0);
|
|
|
|
if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
|
|
/*
|
|
* Although we got a last close, the device may still be in
|
|
* use; e.g. if this was the dialout node, and there are still
|
|
* processes waiting for carrier on the non-dialout node.
|
|
*/
|
|
com_shutdown(sc);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
comread(dev_t dev, struct uio *uio, int flag)
|
|
{
|
|
struct com_softc *sc = device_lookup(&com_cd, COMUNIT(dev));
|
|
struct tty *tp = sc->sc_tty;
|
|
|
|
if (COM_ISALIVE(sc) == 0)
|
|
return (EIO);
|
|
|
|
return ((*tp->t_linesw->l_read)(tp, uio, flag));
|
|
}
|
|
|
|
int
|
|
comwrite(dev_t dev, struct uio *uio, int flag)
|
|
{
|
|
struct com_softc *sc = device_lookup(&com_cd, COMUNIT(dev));
|
|
struct tty *tp = sc->sc_tty;
|
|
|
|
if (COM_ISALIVE(sc) == 0)
|
|
return (EIO);
|
|
|
|
return ((*tp->t_linesw->l_write)(tp, uio, flag));
|
|
}
|
|
|
|
int
|
|
compoll(dev_t dev, int events, struct proc *p)
|
|
{
|
|
struct com_softc *sc = device_lookup(&com_cd, COMUNIT(dev));
|
|
struct tty *tp = sc->sc_tty;
|
|
|
|
if (COM_ISALIVE(sc) == 0)
|
|
return (EIO);
|
|
|
|
return ((*tp->t_linesw->l_poll)(tp, events, p));
|
|
}
|
|
|
|
struct tty *
|
|
comtty(dev_t dev)
|
|
{
|
|
struct com_softc *sc = device_lookup(&com_cd, COMUNIT(dev));
|
|
struct tty *tp = sc->sc_tty;
|
|
|
|
return (tp);
|
|
}
|
|
|
|
int
|
|
comioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
|
|
{
|
|
struct com_softc *sc = device_lookup(&com_cd, COMUNIT(dev));
|
|
struct tty *tp = sc->sc_tty;
|
|
int error;
|
|
int s;
|
|
|
|
if (COM_ISALIVE(sc) == 0)
|
|
return (EIO);
|
|
|
|
error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, p);
|
|
if (error != EPASSTHROUGH)
|
|
return (error);
|
|
|
|
error = ttioctl(tp, cmd, data, flag, p);
|
|
if (error != EPASSTHROUGH)
|
|
return (error);
|
|
|
|
error = 0;
|
|
|
|
s = splserial();
|
|
COM_LOCK(sc);
|
|
|
|
switch (cmd) {
|
|
case TIOCSBRK:
|
|
com_break(sc, 1);
|
|
break;
|
|
|
|
case TIOCCBRK:
|
|
com_break(sc, 0);
|
|
break;
|
|
|
|
case TIOCSDTR:
|
|
com_modem(sc, 1);
|
|
break;
|
|
|
|
case TIOCCDTR:
|
|
com_modem(sc, 0);
|
|
break;
|
|
|
|
case TIOCGFLAGS:
|
|
*(int *)data = sc->sc_swflags;
|
|
break;
|
|
|
|
case TIOCSFLAGS:
|
|
error = suser(p->p_ucred, &p->p_acflag);
|
|
if (error)
|
|
break;
|
|
sc->sc_swflags = *(int *)data;
|
|
break;
|
|
|
|
case TIOCMSET:
|
|
case TIOCMBIS:
|
|
case TIOCMBIC:
|
|
tiocm_to_com(sc, cmd, *(int *)data);
|
|
break;
|
|
|
|
case TIOCMGET:
|
|
*(int *)data = com_to_tiocm(sc);
|
|
break;
|
|
|
|
case PPS_IOC_CREATE:
|
|
break;
|
|
|
|
case PPS_IOC_DESTROY:
|
|
break;
|
|
|
|
case PPS_IOC_GETPARAMS: {
|
|
pps_params_t *pp;
|
|
pp = (pps_params_t *)data;
|
|
*pp = sc->ppsparam;
|
|
break;
|
|
}
|
|
|
|
case PPS_IOC_SETPARAMS: {
|
|
pps_params_t *pp;
|
|
int mode;
|
|
pp = (pps_params_t *)data;
|
|
if (pp->mode & ~ppscap) {
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
sc->ppsparam = *pp;
|
|
/*
|
|
* Compute msr masks from user-specified timestamp state.
|
|
*/
|
|
mode = sc->ppsparam.mode;
|
|
switch (mode & PPS_CAPTUREBOTH) {
|
|
case 0:
|
|
sc->sc_ppsmask = 0;
|
|
break;
|
|
|
|
case PPS_CAPTUREASSERT:
|
|
sc->sc_ppsmask = MSR_DCD;
|
|
sc->sc_ppsassert = MSR_DCD;
|
|
sc->sc_ppsclear = -1;
|
|
break;
|
|
|
|
case PPS_CAPTURECLEAR:
|
|
sc->sc_ppsmask = MSR_DCD;
|
|
sc->sc_ppsassert = -1;
|
|
sc->sc_ppsclear = 0;
|
|
break;
|
|
|
|
case PPS_CAPTUREBOTH:
|
|
sc->sc_ppsmask = MSR_DCD;
|
|
sc->sc_ppsassert = MSR_DCD;
|
|
sc->sc_ppsclear = 0;
|
|
break;
|
|
|
|
default:
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case PPS_IOC_GETCAP:
|
|
*(int*)data = ppscap;
|
|
break;
|
|
|
|
case PPS_IOC_FETCH: {
|
|
pps_info_t *pi;
|
|
pi = (pps_info_t *)data;
|
|
*pi = sc->ppsinfo;
|
|
break;
|
|
}
|
|
|
|
#ifdef PPS_SYNC
|
|
case PPS_IOC_KCBIND: {
|
|
int edge = (*(int *)data) & PPS_CAPTUREBOTH;
|
|
|
|
if (edge == 0) {
|
|
/*
|
|
* remove binding for this source; ignore
|
|
* the request if this is not the current
|
|
* hardpps source
|
|
*/
|
|
if (pps_kc_hardpps_source == sc) {
|
|
pps_kc_hardpps_source = NULL;
|
|
pps_kc_hardpps_mode = 0;
|
|
}
|
|
} else {
|
|
/*
|
|
* bind hardpps to this source, replacing any
|
|
* previously specified source or edges
|
|
*/
|
|
pps_kc_hardpps_source = sc;
|
|
pps_kc_hardpps_mode = edge;
|
|
}
|
|
break;
|
|
}
|
|
#endif /* PPS_SYNC */
|
|
|
|
case TIOCDCDTIMESTAMP: /* XXX old, overloaded API used by xntpd v3 */
|
|
/*
|
|
* Some GPS clocks models use the falling rather than
|
|
* rising edge as the on-the-second signal.
|
|
* The old API has no way to specify PPS polarity.
|
|
*/
|
|
sc->sc_ppsmask = MSR_DCD;
|
|
#ifndef PPS_TRAILING_EDGE
|
|
sc->sc_ppsassert = MSR_DCD;
|
|
sc->sc_ppsclear = -1;
|
|
TIMESPEC_TO_TIMEVAL((struct timeval *)data,
|
|
&sc->ppsinfo.assert_timestamp);
|
|
#else
|
|
sc->sc_ppsassert = -1;
|
|
sc->sc_ppsclear = 0;
|
|
TIMESPEC_TO_TIMEVAL((struct timeval *)data,
|
|
&sc->ppsinfo.clear_timestamp);
|
|
#endif
|
|
break;
|
|
|
|
default:
|
|
error = EPASSTHROUGH;
|
|
break;
|
|
}
|
|
|
|
COM_UNLOCK(sc);
|
|
splx(s);
|
|
|
|
#ifdef COM_DEBUG
|
|
if (com_debug)
|
|
comstatus(sc, "comioctl ");
|
|
#endif
|
|
|
|
return (error);
|
|
}
|
|
|
|
integrate void
|
|
com_schedrx(struct com_softc *sc)
|
|
{
|
|
|
|
sc->sc_rx_ready = 1;
|
|
|
|
/* Wake up the poller. */
|
|
#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
|
|
softintr_schedule(sc->sc_si);
|
|
#else
|
|
#ifndef __NO_SOFT_SERIAL_INTERRUPT
|
|
setsoftserial();
|
|
#else
|
|
if (!com_softintr_scheduled) {
|
|
com_softintr_scheduled = 1;
|
|
callout_reset(&comsoft_callout, 1, comsoft, NULL);
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void
|
|
com_break(struct com_softc *sc, int onoff)
|
|
{
|
|
|
|
if (onoff)
|
|
SET(sc->sc_lcr, LCR_SBREAK);
|
|
else
|
|
CLR(sc->sc_lcr, LCR_SBREAK);
|
|
|
|
if (!sc->sc_heldchange) {
|
|
if (sc->sc_tx_busy) {
|
|
sc->sc_heldtbc = sc->sc_tbc;
|
|
sc->sc_tbc = 0;
|
|
sc->sc_heldchange = 1;
|
|
} else
|
|
com_loadchannelregs(sc);
|
|
}
|
|
}
|
|
|
|
void
|
|
com_modem(struct com_softc *sc, int onoff)
|
|
{
|
|
|
|
if (sc->sc_mcr_dtr == 0)
|
|
return;
|
|
|
|
if (onoff)
|
|
SET(sc->sc_mcr, sc->sc_mcr_dtr);
|
|
else
|
|
CLR(sc->sc_mcr, sc->sc_mcr_dtr);
|
|
|
|
if (!sc->sc_heldchange) {
|
|
if (sc->sc_tx_busy) {
|
|
sc->sc_heldtbc = sc->sc_tbc;
|
|
sc->sc_tbc = 0;
|
|
sc->sc_heldchange = 1;
|
|
} else
|
|
com_loadchannelregs(sc);
|
|
}
|
|
}
|
|
|
|
void
|
|
tiocm_to_com(struct com_softc *sc, u_long how, int ttybits)
|
|
{
|
|
u_char combits;
|
|
|
|
combits = 0;
|
|
if (ISSET(ttybits, TIOCM_DTR))
|
|
SET(combits, MCR_DTR);
|
|
if (ISSET(ttybits, TIOCM_RTS))
|
|
SET(combits, MCR_RTS);
|
|
|
|
switch (how) {
|
|
case TIOCMBIC:
|
|
CLR(sc->sc_mcr, combits);
|
|
break;
|
|
|
|
case TIOCMBIS:
|
|
SET(sc->sc_mcr, combits);
|
|
break;
|
|
|
|
case TIOCMSET:
|
|
CLR(sc->sc_mcr, MCR_DTR | MCR_RTS);
|
|
SET(sc->sc_mcr, combits);
|
|
break;
|
|
}
|
|
|
|
if (!sc->sc_heldchange) {
|
|
if (sc->sc_tx_busy) {
|
|
sc->sc_heldtbc = sc->sc_tbc;
|
|
sc->sc_tbc = 0;
|
|
sc->sc_heldchange = 1;
|
|
} else
|
|
com_loadchannelregs(sc);
|
|
}
|
|
}
|
|
|
|
int
|
|
com_to_tiocm(struct com_softc *sc)
|
|
{
|
|
u_char combits;
|
|
int ttybits = 0;
|
|
|
|
combits = sc->sc_mcr;
|
|
if (ISSET(combits, MCR_DTR))
|
|
SET(ttybits, TIOCM_DTR);
|
|
if (ISSET(combits, MCR_RTS))
|
|
SET(ttybits, TIOCM_RTS);
|
|
|
|
combits = sc->sc_msr;
|
|
if (ISSET(combits, MSR_DCD))
|
|
SET(ttybits, TIOCM_CD);
|
|
if (ISSET(combits, MSR_CTS))
|
|
SET(ttybits, TIOCM_CTS);
|
|
if (ISSET(combits, MSR_DSR))
|
|
SET(ttybits, TIOCM_DSR);
|
|
if (ISSET(combits, MSR_RI | MSR_TERI))
|
|
SET(ttybits, TIOCM_RI);
|
|
|
|
#ifdef COM_PXA2X0
|
|
if (sc->sc_type == COM_TYPE_PXA2x0) {
|
|
if ((sc->sc_ier & 0x0f) != 0)
|
|
SET(ttybits, TIOCM_LE);
|
|
} else
|
|
#endif
|
|
if ((sc->sc_ier & 0xbf) != 0)
|
|
SET(ttybits, TIOCM_LE);
|
|
|
|
return (ttybits);
|
|
}
|
|
|
|
static u_char
|
|
cflag2lcr(tcflag_t cflag)
|
|
{
|
|
u_char lcr = 0;
|
|
|
|
switch (ISSET(cflag, CSIZE)) {
|
|
case CS5:
|
|
SET(lcr, LCR_5BITS);
|
|
break;
|
|
case CS6:
|
|
SET(lcr, LCR_6BITS);
|
|
break;
|
|
case CS7:
|
|
SET(lcr, LCR_7BITS);
|
|
break;
|
|
case CS8:
|
|
SET(lcr, LCR_8BITS);
|
|
break;
|
|
}
|
|
if (ISSET(cflag, PARENB)) {
|
|
SET(lcr, LCR_PENAB);
|
|
if (!ISSET(cflag, PARODD))
|
|
SET(lcr, LCR_PEVEN);
|
|
}
|
|
if (ISSET(cflag, CSTOPB))
|
|
SET(lcr, LCR_STOPB);
|
|
|
|
return (lcr);
|
|
}
|
|
|
|
int
|
|
comparam(struct tty *tp, struct termios *t)
|
|
{
|
|
struct com_softc *sc = device_lookup(&com_cd, COMUNIT(tp->t_dev));
|
|
int ospeed;
|
|
u_char lcr;
|
|
int s;
|
|
|
|
if (COM_ISALIVE(sc) == 0)
|
|
return (EIO);
|
|
|
|
#ifdef COM_HAYESP
|
|
if (sc->sc_type == COM_TYPE_HAYESP) {
|
|
int prescaler, speed;
|
|
|
|
/*
|
|
* Calculate UART clock prescaler. It should be in
|
|
* range of 0 .. 3.
|
|
*/
|
|
for (prescaler = 0, speed = t->c_ospeed; prescaler < 4;
|
|
prescaler++, speed /= 2)
|
|
if ((ospeed = comspeed(speed, sc->sc_frequency,
|
|
sc->sc_type)) > 0)
|
|
break;
|
|
|
|
if (prescaler == 4)
|
|
return (EINVAL);
|
|
sc->sc_prescaler = prescaler;
|
|
} else
|
|
#endif
|
|
ospeed = comspeed(t->c_ospeed, sc->sc_frequency, sc->sc_type);
|
|
|
|
/* Check requested parameters. */
|
|
if (ospeed < 0)
|
|
return (EINVAL);
|
|
if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
|
|
return (EINVAL);
|
|
|
|
/*
|
|
* For the console, always force CLOCAL and !HUPCL, so that the port
|
|
* is always active.
|
|
*/
|
|
if (ISSET(sc->sc_swflags, TIOCFLAG_SOFTCAR) ||
|
|
ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) {
|
|
SET(t->c_cflag, CLOCAL);
|
|
CLR(t->c_cflag, HUPCL);
|
|
}
|
|
|
|
/*
|
|
* If there were no changes, don't do anything. This avoids dropping
|
|
* input and improves performance when all we did was frob things like
|
|
* VMIN and VTIME.
|
|
*/
|
|
if (tp->t_ospeed == t->c_ospeed &&
|
|
tp->t_cflag == t->c_cflag)
|
|
return (0);
|
|
|
|
lcr = ISSET(sc->sc_lcr, LCR_SBREAK) | cflag2lcr(t->c_cflag);
|
|
|
|
s = splserial();
|
|
COM_LOCK(sc);
|
|
|
|
sc->sc_lcr = lcr;
|
|
|
|
/*
|
|
* If we're not in a mode that assumes a connection is present, then
|
|
* ignore carrier changes.
|
|
*/
|
|
if (ISSET(t->c_cflag, CLOCAL | MDMBUF))
|
|
sc->sc_msr_dcd = 0;
|
|
else
|
|
sc->sc_msr_dcd = MSR_DCD;
|
|
/*
|
|
* Set the flow control pins depending on the current flow control
|
|
* mode.
|
|
*/
|
|
if (ISSET(t->c_cflag, CRTSCTS)) {
|
|
sc->sc_mcr_dtr = MCR_DTR;
|
|
sc->sc_mcr_rts = MCR_RTS;
|
|
sc->sc_msr_cts = MSR_CTS;
|
|
sc->sc_efr = EFR_AUTORTS | EFR_AUTOCTS;
|
|
} else if (ISSET(t->c_cflag, MDMBUF)) {
|
|
/*
|
|
* For DTR/DCD flow control, make sure we don't toggle DTR for
|
|
* carrier detection.
|
|
*/
|
|
sc->sc_mcr_dtr = 0;
|
|
sc->sc_mcr_rts = MCR_DTR;
|
|
sc->sc_msr_cts = MSR_DCD;
|
|
sc->sc_efr = 0;
|
|
} else {
|
|
/*
|
|
* If no flow control, then always set RTS. This will make
|
|
* the other side happy if it mistakenly thinks we're doing
|
|
* RTS/CTS flow control.
|
|
*/
|
|
sc->sc_mcr_dtr = MCR_DTR | MCR_RTS;
|
|
sc->sc_mcr_rts = 0;
|
|
sc->sc_msr_cts = 0;
|
|
sc->sc_efr = 0;
|
|
if (ISSET(sc->sc_mcr, MCR_DTR))
|
|
SET(sc->sc_mcr, MCR_RTS);
|
|
else
|
|
CLR(sc->sc_mcr, MCR_RTS);
|
|
}
|
|
sc->sc_msr_mask = sc->sc_msr_cts | sc->sc_msr_dcd;
|
|
|
|
#if 0
|
|
if (ospeed == 0)
|
|
CLR(sc->sc_mcr, sc->sc_mcr_dtr);
|
|
else
|
|
SET(sc->sc_mcr, sc->sc_mcr_dtr);
|
|
#endif
|
|
|
|
sc->sc_dlbl = ospeed;
|
|
sc->sc_dlbh = ospeed >> 8;
|
|
|
|
/*
|
|
* Set the FIFO threshold based on the receive speed.
|
|
*
|
|
* * If it's a low speed, it's probably a mouse or some other
|
|
* interactive device, so set the threshold low.
|
|
* * If it's a high speed, trim the trigger level down to prevent
|
|
* overflows.
|
|
* * Otherwise set it a bit higher.
|
|
*/
|
|
if (sc->sc_type == COM_TYPE_HAYESP)
|
|
sc->sc_fifo = FIFO_DMA_MODE | FIFO_ENABLE | FIFO_TRIGGER_8;
|
|
else if (ISSET(sc->sc_hwflags, COM_HW_FIFO))
|
|
sc->sc_fifo = FIFO_ENABLE |
|
|
(t->c_ospeed <= 1200 ? FIFO_TRIGGER_1 :
|
|
t->c_ospeed <= 38400 ? FIFO_TRIGGER_8 : FIFO_TRIGGER_4);
|
|
else
|
|
sc->sc_fifo = 0;
|
|
|
|
/* And copy to tty. */
|
|
tp->t_ispeed = 0;
|
|
tp->t_ospeed = t->c_ospeed;
|
|
tp->t_cflag = t->c_cflag;
|
|
|
|
if (!sc->sc_heldchange) {
|
|
if (sc->sc_tx_busy) {
|
|
sc->sc_heldtbc = sc->sc_tbc;
|
|
sc->sc_tbc = 0;
|
|
sc->sc_heldchange = 1;
|
|
} else
|
|
com_loadchannelregs(sc);
|
|
}
|
|
|
|
if (!ISSET(t->c_cflag, CHWFLOW)) {
|
|
/* Disable the high water mark. */
|
|
sc->sc_r_hiwat = 0;
|
|
sc->sc_r_lowat = 0;
|
|
if (ISSET(sc->sc_rx_flags, RX_TTY_OVERFLOWED)) {
|
|
CLR(sc->sc_rx_flags, RX_TTY_OVERFLOWED);
|
|
com_schedrx(sc);
|
|
}
|
|
if (ISSET(sc->sc_rx_flags, RX_TTY_BLOCKED|RX_IBUF_BLOCKED)) {
|
|
CLR(sc->sc_rx_flags, RX_TTY_BLOCKED|RX_IBUF_BLOCKED);
|
|
com_hwiflow(sc);
|
|
}
|
|
} else {
|
|
sc->sc_r_hiwat = com_rbuf_hiwat;
|
|
sc->sc_r_lowat = com_rbuf_lowat;
|
|
}
|
|
|
|
COM_UNLOCK(sc);
|
|
splx(s);
|
|
|
|
/*
|
|
* Update the tty layer's idea of the carrier bit, in case we changed
|
|
* CLOCAL or MDMBUF. We don't hang up here; we only do that by
|
|
* explicit request.
|
|
*/
|
|
(void) (*tp->t_linesw->l_modem)(tp, ISSET(sc->sc_msr, MSR_DCD));
|
|
|
|
#ifdef COM_DEBUG
|
|
if (com_debug)
|
|
comstatus(sc, "comparam ");
|
|
#endif
|
|
|
|
if (!ISSET(t->c_cflag, CHWFLOW)) {
|
|
if (sc->sc_tx_stopped) {
|
|
sc->sc_tx_stopped = 0;
|
|
comstart(tp);
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
com_iflush(struct com_softc *sc)
|
|
{
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
#ifdef DIAGNOSTIC
|
|
int reg;
|
|
#endif
|
|
int timo;
|
|
|
|
#ifdef DIAGNOSTIC
|
|
reg = 0xffff;
|
|
#endif
|
|
timo = 50000;
|
|
/* flush any pending I/O */
|
|
while (ISSET(bus_space_read_1(iot, ioh, com_lsr), LSR_RXRDY)
|
|
&& --timo)
|
|
#ifdef DIAGNOSTIC
|
|
reg =
|
|
#else
|
|
(void)
|
|
#endif
|
|
bus_space_read_1(iot, ioh, com_data);
|
|
#ifdef DIAGNOSTIC
|
|
if (!timo)
|
|
printf("%s: com_iflush timeout %02x\n", sc->sc_dev.dv_xname,
|
|
reg);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
com_loadchannelregs(struct com_softc *sc)
|
|
{
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
/* XXXXX necessary? */
|
|
com_iflush(sc);
|
|
|
|
#ifdef COM_PXA2X0
|
|
if (sc->sc_type == COM_TYPE_PXA2x0)
|
|
bus_space_write_1(iot, ioh, com_ier, IER_EUART);
|
|
else
|
|
#endif
|
|
bus_space_write_1(iot, ioh, com_ier, 0);
|
|
|
|
if (ISSET(sc->sc_hwflags, COM_HW_FLOW)) {
|
|
bus_space_write_1(iot, ioh, com_lcr, LCR_EERS);
|
|
bus_space_write_1(iot, ioh, com_efr, sc->sc_efr);
|
|
}
|
|
bus_space_write_1(iot, ioh, com_lcr, sc->sc_lcr | LCR_DLAB);
|
|
bus_space_write_1(iot, ioh, com_dlbl, sc->sc_dlbl);
|
|
bus_space_write_1(iot, ioh, com_dlbh, sc->sc_dlbh);
|
|
bus_space_write_1(iot, ioh, com_lcr, sc->sc_lcr);
|
|
bus_space_write_1(iot, ioh, com_mcr, sc->sc_mcr_active = sc->sc_mcr);
|
|
bus_space_write_1(iot, ioh, com_fifo, sc->sc_fifo);
|
|
#ifdef COM_HAYESP
|
|
if (sc->sc_type == COM_TYPE_HAYESP) {
|
|
bus_space_write_1(iot, sc->sc_hayespioh, HAYESP_CMD1,
|
|
HAYESP_SETPRESCALER);
|
|
bus_space_write_1(iot, sc->sc_hayespioh, HAYESP_CMD2,
|
|
sc->sc_prescaler);
|
|
}
|
|
#endif
|
|
|
|
bus_space_write_1(iot, ioh, com_ier, sc->sc_ier);
|
|
}
|
|
|
|
int
|
|
comhwiflow(struct tty *tp, int block)
|
|
{
|
|
struct com_softc *sc = device_lookup(&com_cd, COMUNIT(tp->t_dev));
|
|
int s;
|
|
|
|
if (COM_ISALIVE(sc) == 0)
|
|
return (0);
|
|
|
|
if (sc->sc_mcr_rts == 0)
|
|
return (0);
|
|
|
|
s = splserial();
|
|
COM_LOCK(sc);
|
|
|
|
if (block) {
|
|
if (!ISSET(sc->sc_rx_flags, RX_TTY_BLOCKED)) {
|
|
SET(sc->sc_rx_flags, RX_TTY_BLOCKED);
|
|
com_hwiflow(sc);
|
|
}
|
|
} else {
|
|
if (ISSET(sc->sc_rx_flags, RX_TTY_OVERFLOWED)) {
|
|
CLR(sc->sc_rx_flags, RX_TTY_OVERFLOWED);
|
|
com_schedrx(sc);
|
|
}
|
|
if (ISSET(sc->sc_rx_flags, RX_TTY_BLOCKED)) {
|
|
CLR(sc->sc_rx_flags, RX_TTY_BLOCKED);
|
|
com_hwiflow(sc);
|
|
}
|
|
}
|
|
|
|
COM_UNLOCK(sc);
|
|
splx(s);
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* (un)block input via hw flowcontrol
|
|
*/
|
|
void
|
|
com_hwiflow(struct com_softc *sc)
|
|
{
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
if (sc->sc_mcr_rts == 0)
|
|
return;
|
|
|
|
if (ISSET(sc->sc_rx_flags, RX_ANY_BLOCK)) {
|
|
CLR(sc->sc_mcr, sc->sc_mcr_rts);
|
|
CLR(sc->sc_mcr_active, sc->sc_mcr_rts);
|
|
} else {
|
|
SET(sc->sc_mcr, sc->sc_mcr_rts);
|
|
SET(sc->sc_mcr_active, sc->sc_mcr_rts);
|
|
}
|
|
bus_space_write_1(iot, ioh, com_mcr, sc->sc_mcr_active);
|
|
}
|
|
|
|
|
|
void
|
|
comstart(struct tty *tp)
|
|
{
|
|
struct com_softc *sc = device_lookup(&com_cd, COMUNIT(tp->t_dev));
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
int s;
|
|
|
|
if (COM_ISALIVE(sc) == 0)
|
|
return;
|
|
|
|
s = spltty();
|
|
if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP))
|
|
goto out;
|
|
if (sc->sc_tx_stopped)
|
|
goto out;
|
|
|
|
if (tp->t_outq.c_cc <= tp->t_lowat) {
|
|
if (ISSET(tp->t_state, TS_ASLEEP)) {
|
|
CLR(tp->t_state, TS_ASLEEP);
|
|
wakeup(&tp->t_outq);
|
|
}
|
|
selwakeup(&tp->t_wsel);
|
|
if (tp->t_outq.c_cc == 0)
|
|
goto out;
|
|
}
|
|
|
|
/* Grab the first contiguous region of buffer space. */
|
|
{
|
|
u_char *tba;
|
|
int tbc;
|
|
|
|
tba = tp->t_outq.c_cf;
|
|
tbc = ndqb(&tp->t_outq, 0);
|
|
|
|
(void)splserial();
|
|
COM_LOCK(sc);
|
|
|
|
sc->sc_tba = tba;
|
|
sc->sc_tbc = tbc;
|
|
}
|
|
|
|
SET(tp->t_state, TS_BUSY);
|
|
sc->sc_tx_busy = 1;
|
|
|
|
/* Enable transmit completion interrupts if necessary. */
|
|
if (!ISSET(sc->sc_ier, IER_ETXRDY)) {
|
|
SET(sc->sc_ier, IER_ETXRDY);
|
|
bus_space_write_1(iot, ioh, com_ier, sc->sc_ier);
|
|
}
|
|
|
|
/* Output the first chunk of the contiguous buffer. */
|
|
if (!ISSET(sc->sc_hwflags, COM_HW_NO_TXPRELOAD)) {
|
|
u_int n;
|
|
|
|
n = sc->sc_tbc;
|
|
if (n > sc->sc_fifolen)
|
|
n = sc->sc_fifolen;
|
|
bus_space_write_multi_1(iot, ioh, com_data, sc->sc_tba, n);
|
|
sc->sc_tbc -= n;
|
|
sc->sc_tba += n;
|
|
}
|
|
COM_UNLOCK(sc);
|
|
out:
|
|
splx(s);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Stop output on a line.
|
|
*/
|
|
void
|
|
comstop(struct tty *tp, int flag)
|
|
{
|
|
struct com_softc *sc = device_lookup(&com_cd, COMUNIT(tp->t_dev));
|
|
int s;
|
|
|
|
s = splserial();
|
|
COM_LOCK(sc);
|
|
if (ISSET(tp->t_state, TS_BUSY)) {
|
|
/* Stop transmitting at the next chunk. */
|
|
sc->sc_tbc = 0;
|
|
sc->sc_heldtbc = 0;
|
|
if (!ISSET(tp->t_state, TS_TTSTOP))
|
|
SET(tp->t_state, TS_FLUSH);
|
|
}
|
|
COM_UNLOCK(sc);
|
|
splx(s);
|
|
}
|
|
|
|
void
|
|
comdiag(void *arg)
|
|
{
|
|
struct com_softc *sc = arg;
|
|
int overflows, floods;
|
|
int s;
|
|
|
|
s = splserial();
|
|
COM_LOCK(sc);
|
|
overflows = sc->sc_overflows;
|
|
sc->sc_overflows = 0;
|
|
floods = sc->sc_floods;
|
|
sc->sc_floods = 0;
|
|
sc->sc_errors = 0;
|
|
COM_UNLOCK(sc);
|
|
splx(s);
|
|
|
|
log(LOG_WARNING, "%s: %d silo overflow%s, %d ibuf flood%s\n",
|
|
sc->sc_dev.dv_xname,
|
|
overflows, overflows == 1 ? "" : "s",
|
|
floods, floods == 1 ? "" : "s");
|
|
}
|
|
|
|
integrate void
|
|
com_rxsoft(struct com_softc *sc, struct tty *tp)
|
|
{
|
|
int (*rint)(int, struct tty *) = tp->t_linesw->l_rint;
|
|
u_char *get, *end;
|
|
u_int cc, scc;
|
|
u_char lsr;
|
|
int code;
|
|
int s;
|
|
|
|
end = sc->sc_ebuf;
|
|
get = sc->sc_rbget;
|
|
scc = cc = com_rbuf_size - sc->sc_rbavail;
|
|
|
|
if (cc == com_rbuf_size) {
|
|
sc->sc_floods++;
|
|
if (sc->sc_errors++ == 0)
|
|
callout_reset(&sc->sc_diag_callout, 60 * hz,
|
|
comdiag, sc);
|
|
}
|
|
|
|
/* If not yet open, drop the entire buffer content here */
|
|
if (!ISSET(tp->t_state, TS_ISOPEN)) {
|
|
get += cc << 1;
|
|
if (get >= end)
|
|
get -= com_rbuf_size << 1;
|
|
cc = 0;
|
|
}
|
|
while (cc) {
|
|
code = get[0];
|
|
lsr = get[1];
|
|
if (ISSET(lsr, LSR_OE | LSR_BI | LSR_FE | LSR_PE)) {
|
|
if (ISSET(lsr, LSR_OE)) {
|
|
sc->sc_overflows++;
|
|
if (sc->sc_errors++ == 0)
|
|
callout_reset(&sc->sc_diag_callout,
|
|
60 * hz, comdiag, sc);
|
|
}
|
|
if (ISSET(lsr, LSR_BI | LSR_FE))
|
|
SET(code, TTY_FE);
|
|
if (ISSET(lsr, LSR_PE))
|
|
SET(code, TTY_PE);
|
|
}
|
|
if ((*rint)(code, tp) == -1) {
|
|
/*
|
|
* The line discipline's buffer is out of space.
|
|
*/
|
|
if (!ISSET(sc->sc_rx_flags, RX_TTY_BLOCKED)) {
|
|
/*
|
|
* We're either not using flow control, or the
|
|
* line discipline didn't tell us to block for
|
|
* some reason. Either way, we have no way to
|
|
* know when there's more space available, so
|
|
* just drop the rest of the data.
|
|
*/
|
|
get += cc << 1;
|
|
if (get >= end)
|
|
get -= com_rbuf_size << 1;
|
|
cc = 0;
|
|
} else {
|
|
/*
|
|
* Don't schedule any more receive processing
|
|
* until the line discipline tells us there's
|
|
* space available (through comhwiflow()).
|
|
* Leave the rest of the data in the input
|
|
* buffer.
|
|
*/
|
|
SET(sc->sc_rx_flags, RX_TTY_OVERFLOWED);
|
|
}
|
|
break;
|
|
}
|
|
get += 2;
|
|
if (get >= end)
|
|
get = sc->sc_rbuf;
|
|
cc--;
|
|
}
|
|
|
|
if (cc != scc) {
|
|
sc->sc_rbget = get;
|
|
s = splserial();
|
|
COM_LOCK(sc);
|
|
|
|
cc = sc->sc_rbavail += scc - cc;
|
|
/* Buffers should be ok again, release possible block. */
|
|
if (cc >= sc->sc_r_lowat) {
|
|
if (ISSET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED)) {
|
|
CLR(sc->sc_rx_flags, RX_IBUF_OVERFLOWED);
|
|
SET(sc->sc_ier, IER_ERXRDY);
|
|
#ifdef COM_PXA2X0
|
|
if (sc->sc_type == COM_TYPE_PXA2x0)
|
|
SET(sc->sc_ier, IER_ERXTOUT);
|
|
#endif
|
|
bus_space_write_1(sc->sc_iot, sc->sc_ioh,
|
|
com_ier, sc->sc_ier);
|
|
}
|
|
if (ISSET(sc->sc_rx_flags, RX_IBUF_BLOCKED)) {
|
|
CLR(sc->sc_rx_flags, RX_IBUF_BLOCKED);
|
|
com_hwiflow(sc);
|
|
}
|
|
}
|
|
COM_UNLOCK(sc);
|
|
splx(s);
|
|
}
|
|
}
|
|
|
|
integrate void
|
|
com_txsoft(struct com_softc *sc, struct tty *tp)
|
|
{
|
|
|
|
CLR(tp->t_state, TS_BUSY);
|
|
if (ISSET(tp->t_state, TS_FLUSH))
|
|
CLR(tp->t_state, TS_FLUSH);
|
|
else
|
|
ndflush(&tp->t_outq, (int)(sc->sc_tba - tp->t_outq.c_cf));
|
|
(*tp->t_linesw->l_start)(tp);
|
|
}
|
|
|
|
integrate void
|
|
com_stsoft(struct com_softc *sc, struct tty *tp)
|
|
{
|
|
u_char msr, delta;
|
|
int s;
|
|
|
|
s = splserial();
|
|
COM_LOCK(sc);
|
|
msr = sc->sc_msr;
|
|
delta = sc->sc_msr_delta;
|
|
sc->sc_msr_delta = 0;
|
|
COM_UNLOCK(sc);
|
|
splx(s);
|
|
|
|
if (ISSET(delta, sc->sc_msr_dcd)) {
|
|
/*
|
|
* Inform the tty layer that carrier detect changed.
|
|
*/
|
|
(void) (*tp->t_linesw->l_modem)(tp, ISSET(msr, MSR_DCD));
|
|
}
|
|
|
|
if (ISSET(delta, sc->sc_msr_cts)) {
|
|
/* Block or unblock output according to flow control. */
|
|
if (ISSET(msr, sc->sc_msr_cts)) {
|
|
sc->sc_tx_stopped = 0;
|
|
(*tp->t_linesw->l_start)(tp);
|
|
} else {
|
|
sc->sc_tx_stopped = 1;
|
|
}
|
|
}
|
|
|
|
#ifdef COM_DEBUG
|
|
if (com_debug)
|
|
comstatus(sc, "com_stsoft");
|
|
#endif
|
|
}
|
|
|
|
#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
|
|
void
|
|
comsoft(void *arg)
|
|
{
|
|
struct com_softc *sc = arg;
|
|
struct tty *tp;
|
|
|
|
if (COM_ISALIVE(sc) == 0)
|
|
return;
|
|
|
|
{
|
|
#else
|
|
void
|
|
#ifndef __NO_SOFT_SERIAL_INTERRUPT
|
|
comsoft(void)
|
|
#else
|
|
comsoft(void *arg)
|
|
#endif
|
|
{
|
|
struct com_softc *sc;
|
|
struct tty *tp;
|
|
int unit;
|
|
#ifdef __NO_SOFT_SERIAL_INTERRUPT
|
|
int s;
|
|
|
|
s = splsoftserial();
|
|
com_softintr_scheduled = 0;
|
|
#endif
|
|
|
|
for (unit = 0; unit < com_cd.cd_ndevs; unit++) {
|
|
sc = device_lookup(&com_cd, unit);
|
|
if (sc == NULL || !ISSET(sc->sc_hwflags, COM_HW_DEV_OK))
|
|
continue;
|
|
|
|
if (COM_ISALIVE(sc) == 0)
|
|
continue;
|
|
|
|
tp = sc->sc_tty;
|
|
if (tp == NULL)
|
|
continue;
|
|
if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0)
|
|
continue;
|
|
#endif
|
|
tp = sc->sc_tty;
|
|
|
|
if (sc->sc_rx_ready) {
|
|
sc->sc_rx_ready = 0;
|
|
com_rxsoft(sc, tp);
|
|
}
|
|
|
|
if (sc->sc_st_check) {
|
|
sc->sc_st_check = 0;
|
|
com_stsoft(sc, tp);
|
|
}
|
|
|
|
if (sc->sc_tx_done) {
|
|
sc->sc_tx_done = 0;
|
|
com_txsoft(sc, tp);
|
|
}
|
|
}
|
|
|
|
#ifndef __HAVE_GENERIC_SOFT_INTERRUPTS
|
|
#ifdef __NO_SOFT_SERIAL_INTERRUPT
|
|
splx(s);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
#ifdef __ALIGN_BRACKET_LEVEL_FOR_CTAGS
|
|
/* there has got to be a better way to do comsoft() */
|
|
}}
|
|
#endif
|
|
|
|
int
|
|
comintr(void *arg)
|
|
{
|
|
struct com_softc *sc = arg;
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
u_char *put, *end;
|
|
u_int cc;
|
|
u_char lsr, iir;
|
|
|
|
if (COM_ISALIVE(sc) == 0)
|
|
return (0);
|
|
|
|
COM_LOCK(sc);
|
|
iir = bus_space_read_1(iot, ioh, com_iir);
|
|
if (ISSET(iir, IIR_NOPEND)) {
|
|
COM_UNLOCK(sc);
|
|
return (0);
|
|
}
|
|
|
|
end = sc->sc_ebuf;
|
|
put = sc->sc_rbput;
|
|
cc = sc->sc_rbavail;
|
|
|
|
again: do {
|
|
u_char msr, delta;
|
|
|
|
lsr = bus_space_read_1(iot, ioh, com_lsr);
|
|
if (ISSET(lsr, LSR_BI)) {
|
|
int cn_trapped = 0;
|
|
|
|
cn_check_magic(sc->sc_tty->t_dev,
|
|
CNC_BREAK, com_cnm_state);
|
|
if (cn_trapped)
|
|
continue;
|
|
#if defined(KGDB) && !defined(DDB)
|
|
if (ISSET(sc->sc_hwflags, COM_HW_KGDB)) {
|
|
kgdb_connect(1);
|
|
continue;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (ISSET(lsr, LSR_RCV_MASK) &&
|
|
!ISSET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED)) {
|
|
while (cc > 0) {
|
|
int cn_trapped = 0;
|
|
put[0] = bus_space_read_1(iot, ioh, com_data);
|
|
put[1] = lsr;
|
|
cn_check_magic(sc->sc_tty->t_dev,
|
|
put[0], com_cnm_state);
|
|
if (cn_trapped) {
|
|
lsr = bus_space_read_1(iot, ioh, com_lsr);
|
|
if (!ISSET(lsr, LSR_RCV_MASK))
|
|
break;
|
|
|
|
continue;
|
|
}
|
|
put += 2;
|
|
if (put >= end)
|
|
put = sc->sc_rbuf;
|
|
cc--;
|
|
|
|
lsr = bus_space_read_1(iot, ioh, com_lsr);
|
|
if (!ISSET(lsr, LSR_RCV_MASK))
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Current string of incoming characters ended because
|
|
* no more data was available or we ran out of space.
|
|
* Schedule a receive event if any data was received.
|
|
* If we're out of space, turn off receive interrupts.
|
|
*/
|
|
sc->sc_rbput = put;
|
|
sc->sc_rbavail = cc;
|
|
if (!ISSET(sc->sc_rx_flags, RX_TTY_OVERFLOWED))
|
|
sc->sc_rx_ready = 1;
|
|
|
|
/*
|
|
* See if we are in danger of overflowing a buffer. If
|
|
* so, use hardware flow control to ease the pressure.
|
|
*/
|
|
if (!ISSET(sc->sc_rx_flags, RX_IBUF_BLOCKED) &&
|
|
cc < sc->sc_r_hiwat) {
|
|
SET(sc->sc_rx_flags, RX_IBUF_BLOCKED);
|
|
com_hwiflow(sc);
|
|
}
|
|
|
|
/*
|
|
* If we're out of space, disable receive interrupts
|
|
* until the queue has drained a bit.
|
|
*/
|
|
if (!cc) {
|
|
SET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED);
|
|
CLR(sc->sc_ier, IER_ERXRDY);
|
|
#ifdef COM_PXA2X0
|
|
if (sc->sc_type == COM_TYPE_PXA2x0)
|
|
CLR(sc->sc_ier, IER_ERXTOUT);
|
|
#endif
|
|
bus_space_write_1(iot, ioh, com_ier,
|
|
sc->sc_ier);
|
|
}
|
|
} else {
|
|
if ((iir & IIR_IMASK) == IIR_RXRDY) {
|
|
#ifdef COM_PXA2X0
|
|
if (sc->sc_type == COM_TYPE_PXA2x0)
|
|
bus_space_write_1(iot, ioh, com_ier,
|
|
IER_EUART);
|
|
else
|
|
#endif
|
|
bus_space_write_1(iot, ioh, com_ier, 0);
|
|
delay(10);
|
|
bus_space_write_1(iot, ioh, com_ier,sc->sc_ier);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
msr = bus_space_read_1(iot, ioh, com_msr);
|
|
delta = msr ^ sc->sc_msr;
|
|
sc->sc_msr = msr;
|
|
/*
|
|
* Pulse-per-second (PSS) signals on edge of DCD?
|
|
* Process these even if line discipline is ignoring DCD.
|
|
*/
|
|
if (delta & sc->sc_ppsmask) {
|
|
struct timeval tv;
|
|
if ((msr & sc->sc_ppsmask) == sc->sc_ppsassert) {
|
|
/* XXX nanotime() */
|
|
microtime(&tv);
|
|
TIMEVAL_TO_TIMESPEC(&tv,
|
|
&sc->ppsinfo.assert_timestamp);
|
|
if (sc->ppsparam.mode & PPS_OFFSETASSERT) {
|
|
timespecadd(&sc->ppsinfo.assert_timestamp,
|
|
&sc->ppsparam.assert_offset,
|
|
&sc->ppsinfo.assert_timestamp);
|
|
}
|
|
|
|
#ifdef PPS_SYNC
|
|
if (pps_kc_hardpps_source == sc &&
|
|
pps_kc_hardpps_mode & PPS_CAPTUREASSERT) {
|
|
hardpps(&tv, tv.tv_usec);
|
|
}
|
|
#endif
|
|
sc->ppsinfo.assert_sequence++;
|
|
sc->ppsinfo.current_mode = sc->ppsparam.mode;
|
|
|
|
} else if ((msr & sc->sc_ppsmask) == sc->sc_ppsclear) {
|
|
/* XXX nanotime() */
|
|
microtime(&tv);
|
|
TIMEVAL_TO_TIMESPEC(&tv,
|
|
&sc->ppsinfo.clear_timestamp);
|
|
if (sc->ppsparam.mode & PPS_OFFSETCLEAR) {
|
|
timespecadd(&sc->ppsinfo.clear_timestamp,
|
|
&sc->ppsparam.clear_offset,
|
|
&sc->ppsinfo.clear_timestamp);
|
|
}
|
|
|
|
#ifdef PPS_SYNC
|
|
if (pps_kc_hardpps_source == sc &&
|
|
pps_kc_hardpps_mode & PPS_CAPTURECLEAR) {
|
|
hardpps(&tv, tv.tv_usec);
|
|
}
|
|
#endif
|
|
sc->ppsinfo.clear_sequence++;
|
|
sc->ppsinfo.current_mode = sc->ppsparam.mode;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Process normal status changes
|
|
*/
|
|
if (ISSET(delta, sc->sc_msr_mask)) {
|
|
SET(sc->sc_msr_delta, delta);
|
|
|
|
/*
|
|
* Stop output immediately if we lose the output
|
|
* flow control signal or carrier detect.
|
|
*/
|
|
if (ISSET(~msr, sc->sc_msr_mask)) {
|
|
sc->sc_tbc = 0;
|
|
sc->sc_heldtbc = 0;
|
|
#ifdef COM_DEBUG
|
|
if (com_debug)
|
|
comstatus(sc, "comintr ");
|
|
#endif
|
|
}
|
|
|
|
sc->sc_st_check = 1;
|
|
}
|
|
} while (!ISSET((iir =
|
|
bus_space_read_1(iot, ioh, com_iir)), IIR_NOPEND) &&
|
|
/*
|
|
* Since some device (e.g., ST16C1550) doesn't clear IIR_TXRDY
|
|
* by IIR read, so we can't do this way: `process all interrupts,
|
|
* then do TX if possble'.
|
|
*/
|
|
(iir & IIR_IMASK) != IIR_TXRDY);
|
|
|
|
/*
|
|
* Read LSR again, since there may be an interrupt between
|
|
* the last LSR read and IIR read above.
|
|
*/
|
|
lsr = bus_space_read_1(iot, ioh, com_lsr);
|
|
|
|
/*
|
|
* See if data can be transmitted as well.
|
|
* Schedule tx done event if no data left
|
|
* and tty was marked busy.
|
|
*/
|
|
if (ISSET(lsr, LSR_TXRDY)) {
|
|
/*
|
|
* If we've delayed a parameter change, do it now, and restart
|
|
* output.
|
|
*/
|
|
if (sc->sc_heldchange) {
|
|
com_loadchannelregs(sc);
|
|
sc->sc_heldchange = 0;
|
|
sc->sc_tbc = sc->sc_heldtbc;
|
|
sc->sc_heldtbc = 0;
|
|
}
|
|
|
|
/* Output the next chunk of the contiguous buffer, if any. */
|
|
if (sc->sc_tbc > 0) {
|
|
u_int n;
|
|
|
|
n = sc->sc_tbc;
|
|
if (n > sc->sc_fifolen)
|
|
n = sc->sc_fifolen;
|
|
bus_space_write_multi_1(iot, ioh, com_data, sc->sc_tba, n);
|
|
sc->sc_tbc -= n;
|
|
sc->sc_tba += n;
|
|
} else {
|
|
/* Disable transmit completion interrupts if necessary. */
|
|
if (ISSET(sc->sc_ier, IER_ETXRDY)) {
|
|
CLR(sc->sc_ier, IER_ETXRDY);
|
|
bus_space_write_1(iot, ioh, com_ier, sc->sc_ier);
|
|
}
|
|
if (sc->sc_tx_busy) {
|
|
sc->sc_tx_busy = 0;
|
|
sc->sc_tx_done = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ISSET((iir = bus_space_read_1(iot, ioh, com_iir)), IIR_NOPEND))
|
|
goto again;
|
|
|
|
COM_UNLOCK(sc);
|
|
|
|
/* Wake up the poller. */
|
|
#ifdef __HAVE_GENERIC_SOFT_INTERRUPTS
|
|
softintr_schedule(sc->sc_si);
|
|
#else
|
|
#ifndef __NO_SOFT_SERIAL_INTERRUPT
|
|
setsoftserial();
|
|
#else
|
|
if (!com_softintr_scheduled) {
|
|
com_softintr_scheduled = 1;
|
|
callout_reset(&comsoft_callout, 1, comsoft, NULL);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if NRND > 0 && defined(RND_COM)
|
|
rnd_add_uint32(&sc->rnd_source, iir | lsr);
|
|
#endif
|
|
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* The following functions are polled getc and putc routines, shared
|
|
* by the console and kgdb glue.
|
|
*
|
|
* The read-ahead code is so that you can detect pending in-band
|
|
* cn_magic in polled mode while doing output rather than having to
|
|
* wait until the kernel decides it needs input.
|
|
*/
|
|
|
|
#define MAX_READAHEAD 20
|
|
static int com_readahead[MAX_READAHEAD];
|
|
static int com_readaheadcount = 0;
|
|
|
|
int
|
|
com_common_getc(dev_t dev, bus_space_tag_t iot, bus_space_handle_t ioh)
|
|
{
|
|
int s = splserial();
|
|
u_char stat, c;
|
|
|
|
/* got a character from reading things earlier */
|
|
if (com_readaheadcount > 0) {
|
|
int i;
|
|
|
|
c = com_readahead[0];
|
|
for (i = 1; i < com_readaheadcount; i++) {
|
|
com_readahead[i-1] = com_readahead[i];
|
|
}
|
|
com_readaheadcount--;
|
|
splx(s);
|
|
return (c);
|
|
}
|
|
|
|
/* block until a character becomes available */
|
|
while (!ISSET(stat = bus_space_read_1(iot, ioh, com_lsr), LSR_RXRDY))
|
|
;
|
|
|
|
c = bus_space_read_1(iot, ioh, com_data);
|
|
stat = bus_space_read_1(iot, ioh, com_iir);
|
|
{
|
|
int cn_trapped = 0; /* unused */
|
|
#ifdef DDB
|
|
extern int db_active;
|
|
if (!db_active)
|
|
#endif
|
|
cn_check_magic(dev, c, com_cnm_state);
|
|
}
|
|
splx(s);
|
|
return (c);
|
|
}
|
|
|
|
void
|
|
com_common_putc(dev_t dev, bus_space_tag_t iot, bus_space_handle_t ioh, int c)
|
|
{
|
|
int s = splserial();
|
|
int cin, stat, timo;
|
|
|
|
if (com_readaheadcount < MAX_READAHEAD
|
|
&& ISSET(stat = bus_space_read_1(iot, ioh, com_lsr), LSR_RXRDY)) {
|
|
int cn_trapped = 0;
|
|
cin = bus_space_read_1(iot, ioh, com_data);
|
|
stat = bus_space_read_1(iot, ioh, com_iir);
|
|
cn_check_magic(dev, cin, com_cnm_state);
|
|
com_readahead[com_readaheadcount++] = cin;
|
|
}
|
|
|
|
/* wait for any pending transmission to finish */
|
|
timo = 150000;
|
|
while (!ISSET(bus_space_read_1(iot, ioh, com_lsr), LSR_TXRDY) && --timo)
|
|
continue;
|
|
|
|
bus_space_write_1(iot, ioh, com_data, c);
|
|
COM_BARRIER(iot, ioh, BR | BW);
|
|
|
|
/* wait for this transmission to complete */
|
|
timo = 1500000;
|
|
while (!ISSET(bus_space_read_1(iot, ioh, com_lsr), LSR_TXRDY) && --timo)
|
|
continue;
|
|
|
|
splx(s);
|
|
}
|
|
|
|
/*
|
|
* Initialize UART for use as console or KGDB line.
|
|
*/
|
|
int
|
|
cominit(bus_space_tag_t iot, bus_addr_t iobase, int rate, int frequency,
|
|
int type, tcflag_t cflag, bus_space_handle_t *iohp)
|
|
{
|
|
bus_space_handle_t ioh;
|
|
|
|
if (bus_space_map(iot, iobase, COM_NPORTS, 0, &ioh))
|
|
return (ENOMEM); /* ??? */
|
|
|
|
bus_space_write_1(iot, ioh, com_lcr, LCR_EERS);
|
|
bus_space_write_1(iot, ioh, com_efr, 0);
|
|
bus_space_write_1(iot, ioh, com_lcr, LCR_DLAB);
|
|
rate = comspeed(rate, frequency, type);
|
|
bus_space_write_1(iot, ioh, com_dlbl, rate);
|
|
bus_space_write_1(iot, ioh, com_dlbh, rate >> 8);
|
|
bus_space_write_1(iot, ioh, com_lcr, cflag2lcr(cflag));
|
|
bus_space_write_1(iot, ioh, com_mcr, MCR_DTR | MCR_RTS);
|
|
bus_space_write_1(iot, ioh, com_fifo,
|
|
FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_1);
|
|
#ifdef COM_PXA2X0
|
|
if (type == COM_TYPE_PXA2x0)
|
|
bus_space_write_1(iot, ioh, com_ier, IER_EUART);
|
|
else
|
|
#endif
|
|
bus_space_write_1(iot, ioh, com_ier, 0);
|
|
|
|
*iohp = ioh;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Following are all routines needed for COM to act as console
|
|
*/
|
|
struct consdev comcons = {
|
|
NULL, NULL, comcngetc, comcnputc, comcnpollc, NULL, NULL, NULL,
|
|
NODEV, CN_NORMAL
|
|
};
|
|
|
|
|
|
int
|
|
comcnattach(bus_space_tag_t iot, bus_addr_t iobase, int rate, int frequency,
|
|
int type, tcflag_t cflag)
|
|
{
|
|
int res;
|
|
|
|
res = cominit(iot, iobase, rate, frequency, type, cflag, &comconsioh);
|
|
if (res)
|
|
return (res);
|
|
|
|
cn_tab = &comcons;
|
|
cn_init_magic(&com_cnm_state);
|
|
cn_set_magic("\047\001"); /* default magic is BREAK */
|
|
|
|
comconstag = iot;
|
|
comconsaddr = iobase;
|
|
comconsrate = rate;
|
|
comconscflag = cflag;
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
comcngetc(dev_t dev)
|
|
{
|
|
|
|
return (com_common_getc(dev, comconstag, comconsioh));
|
|
}
|
|
|
|
/*
|
|
* Console kernel output character routine.
|
|
*/
|
|
void
|
|
comcnputc(dev_t dev, int c)
|
|
{
|
|
|
|
com_common_putc(dev, comconstag, comconsioh, c);
|
|
}
|
|
|
|
void
|
|
comcnpollc(dev_t dev, int on)
|
|
{
|
|
|
|
}
|
|
|
|
#ifdef KGDB
|
|
int
|
|
com_kgdb_attach(bus_space_tag_t iot, bus_addr_t iobase, int rate,
|
|
int frequency, int type, tcflag_t cflag)
|
|
{
|
|
int res;
|
|
|
|
if (iot == comconstag && iobase == comconsaddr) {
|
|
#if !defined(DDB)
|
|
return (EBUSY); /* cannot share with console */
|
|
#else
|
|
com_kgdb_ioh = comconsioh;
|
|
#endif
|
|
} else {
|
|
res = cominit(iot, iobase, rate, frequency, type, cflag,
|
|
&com_kgdb_ioh);
|
|
if (res)
|
|
return (res);
|
|
|
|
/*
|
|
* XXXfvdl this shouldn't be needed, but the cn_magic goo
|
|
* expects this to be initialized
|
|
*/
|
|
cn_init_magic(&com_cnm_state);
|
|
cn_set_magic("\047\001");
|
|
}
|
|
|
|
kgdb_attach(com_kgdb_getc, com_kgdb_putc, NULL);
|
|
kgdb_dev = 123; /* unneeded, only to satisfy some tests */
|
|
|
|
com_kgdb_iot = iot;
|
|
com_kgdb_addr = iobase;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
int
|
|
com_kgdb_getc(void *arg)
|
|
{
|
|
|
|
return (com_common_getc(NODEV, com_kgdb_iot, com_kgdb_ioh));
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
void
|
|
com_kgdb_putc(void *arg, int c)
|
|
{
|
|
|
|
com_common_putc(NODEV, com_kgdb_iot, com_kgdb_ioh, c);
|
|
}
|
|
#endif /* KGDB */
|
|
|
|
/* helper function to identify the com ports used by
|
|
console or KGDB (and not yet autoconf attached) */
|
|
int
|
|
com_is_console(bus_space_tag_t iot, bus_addr_t iobase, bus_space_handle_t *ioh)
|
|
{
|
|
bus_space_handle_t help;
|
|
|
|
if (!comconsattached &&
|
|
iot == comconstag && iobase == comconsaddr)
|
|
help = comconsioh;
|
|
#ifdef KGDB
|
|
else if (!com_kgdb_attached &&
|
|
iot == com_kgdb_iot && iobase == com_kgdb_addr)
|
|
help = com_kgdb_ioh;
|
|
#endif
|
|
else
|
|
return (0);
|
|
|
|
if (ioh)
|
|
*ioh = help;
|
|
return (1);
|
|
}
|