/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Asynchronous protocol handler for Z8530 chips
* Handles normal UNIX support for terminals & modems
*/
#include <sys/sysmacros.h>
#include <sys/ser_async.h>
/*
* PPS (Pulse Per Second) support.
*/
extern void ddi_hardpps(struct timeval *, int);
#ifdef PPSCLOCKLED
/* XXX Use these to observe PPS latencies and jitter on a scope */
#define LED_ON
#define LED_OFF
#else
#define LED_ON
#define LED_OFF
#endif
0,
#ifdef lint
#else
#endif
};
3, /* 0 */
3, /* 1 */
3, /* 2 */
3, /* 3 */
3, /* 4 */
3, /* 5 */
3, /* 6 */
3, /* 7 */
3, /* 8 */
3, /* 9 */
3, /* 10 */
3, /* 11 */
3, /* 12 */
3, /* 13 */
2, /* 14 */
1, /* 15 */
1, /* 16 */
1 /* 17 */
};
0,
#ifdef lint
#else
#endif
};
3, /* 0 */
3, /* 1 */
3, /* 2 */
3, /* 3 */
3, /* 4 */
3, /* 5 */
3, /* 6 */
2, /* 7 */
2, /* 8 */
2, /* 9 */
2, /* 10 */
1, /* 11 */
1, /* 12 */
1, /* 13 */
1, /* 14 */
1, /* 15 */
1, /* 16 */
1 /* 17 */
};
0, /* 0 */
1, /* 1 */
1, /* 2 */
1, /* 3 */
1, /* 4 */
1, /* 5 */
1, /* 6 */
1, /* 7 */
1, /* 8 */
1, /* 9 */
1, /* 10 */
1, /* 11 */
1, /* 12 */
3, /* 13 */
3, /* 14 */
4, /* 15 */
4, /* 16 */
4 /* 17 */
};
/*
* Special macros to handle STREAMS operations.
* These are required to address memory leakage problems.
* WARNING : the macro do NOT call ZSSETSOFT
*/
/*
* Should be called holding only the adaptive (zs_excl) mutex.
*/
{ \
register int n = zsa_rstandby; \
while (--n >= 0 && allocbcount > 0) { \
if (!za->za_rstandby[n]) { \
BPRI_MED, \
zsa_callback, zs); \
break; \
} \
} \
allocbcount--; \
} \
} \
register int usedcnt = 0; \
for (n = 0; n < zsa_rstandby; n++) \
if (!za->za_rstandby[n]) \
usedcnt++; \
} \
} \
}
/*
* Should be called holding the spin (zs_excl_hi) mutex.
*/
{ \
register int n = zsa_rstandby; \
while (--n >= 0) { \
break; \
} \
} \
if (!mp) { \
register int usedcnt = 0; \
for (n = 0; n < zsa_rstandby; n++) \
if (!za->za_rstandby[n]) \
usedcnt++; \
} \
} \
}
/*
* Should get the spin (zs_excl_hi) mutex.
*/
{ \
}
/*
* Should be called holding the spin (zs_excl_hi) mutex.
*/
{ \
} else \
} \
}
/*
* Should be called holding the spin (zs_excl_hi) mutex.
*/
#define ZSA_KICK_RCV \
{ \
if (mp) { \
} \
} \
}
{ \
} else { \
} \
}
/*
* Should be called holding only the adaptive (zs_excl) mutex.
*/
{ \
za->za_rdone_rptr = 0; \
} else { \
} \
}
/*
* Should be called holding only the adaptive (zs_excl) mutex.
*/
#define ZSA_FLUSHQ \
{ \
for (;;) { \
if (!(tmp)) \
break; \
} \
}
/*
* Logging definitions
*/
#ifdef ZSA_DEBUG
#ifdef ZS_DEBUG_ALL
extern char zs_h_log[];
extern int zs_h_log_n;
#define zsa_h_log_clear
#define zsa_h_log_add(c) \
{ \
if (zs_h_log_n >= ZS_H_LOG_MAX) \
zs_h_log_n = 0; \
zs_h_log[zs_h_log_n++] = c; \
}
#else /* ZS_DEBUG_ALL */
#define zsa_h_log_add(c) \
{ \
}
#define zsa_h_log_clear \
{ \
register char *p; \
*p-- = '\0'; \
}
#endif /* ZS_DEBUG_ALL */
{ \
}
#else /* ZSA_DEBUG */
#define zsa_h_log_clear
#define zsa_h_log_add(c)
#endif /* ZSA_DEBUG */
0,
"zs",
0,
2048,
128
};
putq,
(int (*)())zsa_rsrv,
NULL,
};
(int (*)())zsa_wput,
NULL,
NULL,
NULL,
NULL,
};
NULL,
NULL,
};
/*
* The async interrupt entry points.
*/
static void
{
/* LINTED */
register short c;
c = SCC_READDATA();
ZSDELAY();
}
/*ARGSUSED*/
static int
{
return (0);
}
};
};
static void zsa_restart(void *);
static void zsa_reioctl(void *);
static void zsa_kick_rcv(void *);
static void zsa_callback(void *);
/* ARGSUSED */
int
void **result)
{
return (DDI_FAILURE);
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
error = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2INSTANCE:
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
/*
* The Asynchronous Driver.
*/
/*
* Determine if the zsminor device is in use as either a stdin or stdout
* device, so we can be careful about how we initialize the DUART, if
* it is, in fact, in use.
*
* Since this is expensive, we do it once and store away the answers,
* since this gets called a number of times per phyical zs device.
* Perhaps, this should be in a loadable module, so it can get thrown
* away after all the zs devices are attached?
*/
/*
* To determine if a given unit is being used by the PROM,
* to zs internal minor device numbers:
*
* PROM (real device) zs minor device
*
* "zs", 0, "a" 0 ttya
* "zs", 0, "b" 1 ttyb
* "zs", 1, "a" 2 keyboard
* "zs", 1, "b" 3 mouse
* "zs", 2, "a" 4 ttyc
* "zs", 2, "b" 5 ttyd
*
* The following value mapping lines assume that insource
* and outsink map as "screen, a, b, c, d, ...", and that
* zs minors are "a, b, kbd, mouse, c, d, ...".
*/
int
{
char *stdioname;
/*
* Basically, get my name and compare it to stdio devnames
* and if we get a match, then the device is in use as either
* stdin or stdout device (console tip line or keyboard device).
*
* We get two forms of the pathname, one complete with the
* the channel number, and if the channel is 'a', then
* we also deal with the user's ability to default to
* channel 'a', by omitting the channel number option.
* We then compare these pathnames to both the stdin and
* stdout pathnames. If any of these match, then the channel
* is in use.
*/
default_pathname[0] = (char)0; /* default pathname if channel 'a' */
if ((zsminor & 1) == 0)
minordata[0] = ':';
minordata[2] = (char)0;
stdioname = prom_stdinpath();
return (1);
}
return (1);
}
stdioname = prom_stdoutpath();
return (1);
}
return (1);
}
return (0);
}
/*
* Initialize zs
*/
void
{
/*
* This routine is called near the end of the zs module's attach
* process. It initializes the TTY protocol-private data for this
* channel that needs to be in place before interrupts are enabled.
*/
/*
* Raise modem control lines on serial ports associated
* with the console and (optionally) softcarrier lines.
* Drop modem control lines on all others so that modems
* will not answer and portselectors will skip these
* lines until they are opened by a getty.
*/
else
if (zsa_rstandby > ZSA_MAX_RSTANDBY)
if (zsa_rdone > ZSA_RDONE_MAX)
}
/*
* Open routine.
*/
/*ARGSUSED*/
static int
{
int len;
return (ENXIO); /* unit not configured */
/* zscom is allocated by zsattach, and thus cannot be NULL here */
return (ENXIO); /* device not found by autoconfig */
}
return (EBUSY); /* another protocol got here first */
}
if (zs->zs_suspended) {
}
/* Mark device as busy (for power management) */
za->za_rdone_wptr = 0;
za->za_rdone_rptr = 0;
}
/*
* Block waiting for carrier to come up,
* unless this is a no-delay open.
*/
/*
* Get the default termios settings (cflag).
* These are stored as a property in the
* "options" node.
*/
ddi_root_node(), 0, "ttymodes",
} else {
/*
* Gack! Whine about it.
*/
"zs: Couldn't get ttymodes property!");
}
} else {
}
}
za->za_overrun = 0;
za->za_wbufcid = 0;
secpolicy_excl_open(cr) != 0) {
return (EBUSY);
return (EBUSY);
}
/*
* Check carrier.
*/
/*
* If FNDELAY and FNONBLOCK are clear, block until carrier up.
* Quit on interrupt.
*/
if (zs->zs_suspended) {
0, 1);
}
return (EINTR);
}
goto again;
else {
return (EBUSY);
}
}
return (EBUSY);
}
return (0);
}
static void
{
/*
* We define "progress" as either waiting on a timed break or delay, or
* having had at least one transmitter interrupt. If none of these are
* true, then just terminate the output and wake up that close thread.
*/
/*
* Since this timer is running, we know that we're in exit(2).
* That means that the user can't possibly be waiting on any
* valid ioctl(2) completion anymore, and we should just flush
* everything.
*/
} else {
}
}
/*
* Close routine.
*
* Important locking note: the zs_ocexcl lock is not held at all in this
* routine. This is intentional. That lock is used to coordinate multiple
* simultaneous opens on a stream, and there's no such thing as multiple
* simultaneous closes on a stream.
*/
/*ARGSUSED*/
static int
{
int i;
int tmp;
/*
* There are two flavors of break -- timed (M_BREAK or TCSBRK) and
* untimed (TIOCSBRK). For the timed case, these are enqueued on our
* write queue and there's a timer running, so we don't have to worry
* about them. For the untimed case, though, the user obviously made a
* mistake, because these are handled immediately. We'll terminate the
* break now and honor his implicit request by discarding the rest of
* the data.
*/
goto nodrain;
/*
* If the user told us not to delay the close ("non-blocking"), then
* don't bother trying to drain.
*
* If the user did M_STOP (ASYNC_STOPPED), there's no hope of ever
* getting an M_START (since these messages aren't enqueued), and the
* only other way to clear the stop condition is by loss of DCD, which
* would discard the queue data. Thus, we drop the output data if
* ASYNC_STOPPED is set.
*/
goto nodrain;
/*
* If there's any pending output, then we have to try to drain it.
* There are two main cases to be handled:
* - called by close(2): need to drain until done or until
* a signal is received. No timeout.
* - called by exit(2): need to drain while making progress
* or until a timeout occurs. No signals.
*
* If we can't rely on receiving a signal to get us out of a hung
* session, then we have to use a timer. In this case, we set a timer
* to check for progress in sending the output data -- all that we ask
* (at each interval) is that there's been some progress made. Since
* the interrupt routine grabs buffers from the write queue, we can't
* trust changes in zs_wr_cur. Instead, we use a progress flag.
*
* Note that loss of carrier will cause the output queue to be flushed,
* and we'll wake up again and finish normally.
*/
if (!ddi_can_receive_sig() && zs_drain_check != 0) {
}
break;
}
}
/*
* If break is in progress, stop it.
*/
}
/*
* If line has HUPCL set or is incompletely opened,
* and it is not the console or the keyboard,
* fix up the modem lines.
*/
/*
* Nobody, zsh or zs can now open this port until
* zsopinit(zs, &zsops_null);
*
*/
/*
* If DTR is being held high by softcarrier,
* set up the ZS_ON set; if not, hang up.
*/
else
/*
* Don't let an interrupt in the middle of close
* bounce us back to the top; just continue
* closing as if nothing had happened.
*/
if (zs->zs_suspended) {
}
if (tmp == 0)
goto out;
}
/*
* If nobody's now using it, turn off receiver interrupts.
*/
out:
/*
* Clear out device state.
*/
if (bp)
if (bp)
for (i = 0; i < zsa_rstandby; i++) {
if (bp)
}
}
if (zs->zs_suspended) {
}
qprocsoff(q);
/*
* Cancel outstanding "bufcall" request.
*/
if (za_wbufcid)
if (za_bufcid)
/*
* Cancel outstanding timeout.
*/
if (za_zsa_restart_id)
(void) untimeout(za_zsa_restart_id);
if (za_kick_rcv_id)
(void) untimeout(za_kick_rcv_id);
/* Mark device as available for power management */
return (0);
}
/*
* Put procedure for write queue.
* Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
* set the flow control character for M_STOPI and M_STARTI messages;
* queue up M_BREAK, M_DELAY, and M_DATA messages for processing
* by the start routine, and then call the start routine; discard
* everything else. Note that this driver does not incorporate any
* mechanism to negotiate to handle the canonicalization process.
* It expects that these functions are handled in upper module(s),
* as we do in ldterm.
*/
static void
{
int error;
(void) zsa_softint(zs);
}
case M_STOP:
/*
* Since we don't do real DMA, we can just let the
* chip coast to a stop after applying the brakes.
*/
}
if (bp)
break;
case M_START:
/*
* If an output operation is in progress,
* resume it. Otherwise, prod the start
* routine.
*/
}
break;
case M_IOCTL:
case TIOCGPPS:
/*
* Get PPS state.
*/
ZSA_QREPLY(q, mp);
break;
}
else
ZSA_QREPLY(q, mp);
break;
case TIOCSPPS:
/*
* Set PPS state.
*/
if (error != 0) {
ZSA_QREPLY(q, mp);
break;
}
ZSA_QREPLY(q, mp);
break;
case TIOCGPPSEV:
{
/*
* Get PPS event data.
*/
void *buf;
#ifdef _SYSCALL32_IMPL
#endif
}
ZSA_QREPLY(q, mp);
break;
}
#ifdef _SYSCALL32_IMPL
} else
#endif
{
buf = &ppsclockev;
}
ZSA_QREPLY(q, mp);
break;
}
ZSA_QREPLY(q, mp);
break;
}
case TCSETSW:
case TCSETSF:
case TCSETAW:
case TCSETAF:
case TCSBRK:
/*
* The changes do not take effect until all
* output queued before them is drained.
* Put this message on the queue, so that
* "zsa_start" will see it when it's done
* with the output before it. Poke the
* start routine, just in case.
*/
break;
default:
/*
* Do it now.
*/
break;
}
break;
case M_IOCDATA:
/*
* Just free message on failure.
*/
break;
}
case TIOCMSET:
DMSET);
ZSA_QREPLY(q, mp);
break;
case TIOCMBIS:
DMBIS);
ZSA_QREPLY(q, mp);
break;
case TIOCMBIC:
DMBIC);
ZSA_QREPLY(q, mp);
break;
case TIOCMGET:
ZSA_QREPLY(q, mp);
break;
default:
}
break;
case M_FLUSH:
/*
* Abort any output in progress.
*/
if (bp)
}
/*
* Flush our write queue.
*/
}
/*
* Flush any data in the temporary receive buffer
*/
} else {
if (!(SCC_READ0() & ZSRR0_RX_READY)) {
/*
* settle time for 1 character shift
*/
}
while ((SCC_READ0() &
(ZSRR0_CD | ZSRR0_RX_READY)) ==
/*
* Empty Receiver
*/
(void) SCC_READDATA();
}
}
ZSA_QREPLY(q, mp);
/*
* give the read queues a crack at it
*/
} else
/*
* We must make sure we process messages that survive the
* write-side flush. Without this call, the close protocol
* with ldterm can hang forever. (ldterm will have sent us a
* TCSBRK ioctl that it expects a response to.)
*/
break;
case M_BREAK:
case M_DELAY:
case M_DATA:
/*
* Queue the message up to be transmitted,
* and poke the start routine.
*/
break;
case M_STOPI:
}
if (bp)
else
break;
case M_STARTI:
}
if (bp)
else
break;
case M_CTL:
} else {
/*
* These MC_SERVICE type messages are used by upper
* modules to tell this driver to send input up
* immediately, or that it can wait for normal
* processing that may or may not be done. Sun
* requires these for the mouse module.
*/
case MC_SERVICEIMM:
break;
case MC_SERVICEDEF:
break;
}
}
break;
default:
/*
* "No, I don't want a subscription to Chain Store Age,
* thank you anyway."
*/
break;
}
}
/*
* zs read service procedure
*/
static void
{
}
}
/*
* Transmitter interrupt service routine.
* If there's more data to transmit in the current pseudo-DMA block,
* and the transmitter is ready, send the next character if output
* is not stopped or draining.
* Otherwise, queue up a soft interrupt.
*/
static void
{
return;
}
SCC_WRITEDATA(*wr_cur++);
#ifdef ZSA_DEBUG
#endif
return;
} else {
/*
* Use the rcv_flags_mask as it is set and
* test while holding the zs_excl_hi mutex
*/
return;
}
}
return;
}
return;
}
/*
* Set DO_TRANSMIT bit so that the soft interrupt can
* test it and unset the ZAS_BUSY in za_flags while holding
* the mutex zs_excl and zs_excl_hi
*/
}
/*
*/
static void
{
ZSA_R0_LOG(s0);
/*
* PPS (Pulse Per Second) support.
*/
/*
* This code captures a timestamp at the designated
* transition of the PPS signal (CD asserted). The
* code provides a pointer to the timestamp, as well
* as the hardware counter value at the capture.
*
* Note: the kernel has nano based time values while
* NTP requires micro based, an in-line fast algorithm
* to convert nsec to usec is used here -- see hrt2ts()
*/
gethrestime(&ts);
++ppsclockev.serial;
/*
* Because the kernel keeps a high-resolution time, pass the
* current highres timestamp in tvp and zero in usec.
*/
ddi_hardpps(tvp, 0);
}
#ifdef SLAVIO_BUG
/*
* ZSRR0_BREAK turned off. This means that the break sequence
* has completed (i.e., the stop bit finally arrived).
*/
if ((s0 & ZSRR0_RX_READY) == 0) {
/*
* SLAVIO will generate a separate STATUS change
* interrupt when the break sequence completes.
* SCC will combine both, taking the higher priority
* a SLAVIO.
*/
} else {
/*
* The NUL character in the receiver is part of the
* break sequence; it is discarded.
*/
(void) SCC_READDATA(); /* swallow null */
}
#else /* SLAVIO_BUG */
/*
* ZSRR0_BREAK turned off. This means that the break sequence
* has completed (i.e., the stop bit finally arrived). The NUL
* character in the receiver is part of the break sequence;
* it is discarded.
*/
(void) SCC_READDATA(); /* swallow null */
#endif /* SLAVIO_BUG */
/*
* Note: this will cause an abort if a break occurs on
* the "keyboard device", regardless of whether the
* "keyboard device" is a real keyboard or just a
* terminal on a serial line. This permits you to
* abort a workstation by unplugging the keyboard,
* even if the normal abort key sequence isn't working.
*/
(abort_enable != KIOCABORTALTERNATE)) {
abort_sequence_enter((char *)NULL);
/*
* We just broke into the monitor or debugger,
* ignore the break in this case so whatever
* random program that was running doesn't get
* a SIGINT.
*/
return;
}
}
/*
* If hardware flow control is enabled, (re)start output
* when CTS is reasserted.
*/
}
/*
* Receive Interrupt
*/
static void
{
register uchar_t c;
#ifdef ZSA_DEBUG
#endif
/*
* Check for character break sequence
*/
if (abort_charseq_recognize(c))
abort_sequence_enter((char *)NULL);
}
if (!rd_cur) {
#ifdef SLAVIO_BUG
/*
* SLAVIO generates FE for the start of break and
* during break when parity is set. End of break is
* detected when the first character is received.
* This character is always garbage and is thrown away.
*/
if (za->za_slav_break) {
za->za_slav_break = 0;
return;
}
#endif /* SLAVIO_BUG */
/*
* A break sequence was under way, and a NUL character
* was received. Discard the NUL character, as it is
* part of the break sequence; if ZSRR0_BREAK turned
* off, indicating that the break sequence has com-
* pleted, call "zsa_xsint" to properly handle the
* interrupts get lost occasionally, so this ensures
* that one is delivered.
*/
c = SCC_READ0();
if (!(c & ZSRR0_BREAK))
return;
}
#ifdef SLAVIO_BUG
if (c == 0 && za->za_breakoff) {
/*
* A break sequence completed, but SLAVIO generates
* the NULL character interrupt late, so we throw the
* NULL away now.
*/
return;
}
/*
* make sure it gets cleared.
*/
za->za_breakoff = 0;
#endif /* SLAVIO_BUG */
ZSA_KICK_RCV; /* We can have M_BREAK msg */
ZSA_ALLOCB(bp);
if (!bp) {
za->za_sw_overrun++;
return;
}
if (za->za_kick_rcv_id == 0)
}
ZSA_ALLOCB(bp);
if (!bp) {
za->za_sw_overrun++;
return;
}
}
*rd_cur++ = c;
}
*rd_cur++ = c;
}
/*
* Send the data up immediately
*/
}
}
/*
* Special receive condition interrupt handler.
*/
static void
{
register short s1;
register uchar_t c;
c = SCC_READDATA(); /* swallow bad character */
}
#ifdef SLAVIO_BUG
/*
* SLAVIO does not handle breaks properly when parity is enabled.
*
* In general, if a null character is received when a framing
* error occurs then it is a break condition and not a real
* framing error. The null character must be limited to the
* number of bits including the parity bit. For example, a 6
* bit character with parity would be null if the lower 7 bits
* read from the receive fifo were 0. (The higher order bits are
* general rule would be an 8 bit null character with parity being
* a 1 in the parity bit and a framing error. This exception
* can be determined by examining the parity error bit in RREG 1.
*
* A null character, even parity, 8 bits, no parity error,
* (0 0000 0000) with framing error is a break condition.
*
* A null character, even parity, 8 bits, parity error,
* (1 0000 0000) with framing error is a framing error.
*
* A null character, odd parity, 8 bits, parity error
* (0 0000 0000) with framing error is a break condition.
*
* A null character, odd parity, 8 bits, no parity error,
* (1 0000 0000) with framing error is a framing error.
*/
case CS5:
c1 = c & 0x3f;
break;
case CS6:
c1 = c & 0x7f;
break;
case CS7:
c1 = c & 0xff;
break;
case CS8:
c1 = 0xff;
c1 = 0xff;
else
c1 = c;
break;
}
/*
* We fake start of break condition.
*/
return;
}
}
#endif /* SLAVIO_BUG */
/*
* Mark the parity error so zsa_process will
* notice it and send it up in an M_BREAK
* message; ldterm will do the actual parity error
* processing
*/
}
if (!bp)
ZSA_ALLOCB(bp);
if (!bp) {
za->za_sw_overrun++;
} else {
*rd_cur++ = c;
}
}
za->za_hw_overrun++;
}
}
/*
* Process software interrupts (or poll)
* Crucial points:
* 3. BUG - breaks are handled "out-of-band" - their relative position
* among input events is lost, as well as multiple breaks together.
* This is probably not a problem in practice.
*/
static int
{
register int m_error;
register int allocbcount = 0;
register int do_ttycommon_qfull = 0;
register queue_t *q;
return (0);
}
/*
* carrier up?
*/
/*
* carrier present
*/
return (0);
}
}
}
return (0);
}
if (!q) {
return (0);
}
za->za_m_error = 0;
if (za->za_do_kick_rcv_in_softint) {
za->za_do_kick_rcv_in_softint = 0;
}
while (!za_kick_active) {
if (!bp)
break;
allocbcount++;
if (!(canputnext(q))) {
if (za->za_grace_flow_control >=
CRTSXOFF) {
allocbcount--;
break;
}
do_ttycommon_qfull = 1;
continue;
} else
} else
za->za_grace_flow_control = 0;
}
if (!head) {
} else {
if (!tail)
}
}
if (allocbcount)
/*
* carrier up?
*/
/*
* carrier present
*/
}
} else {
/*
* Carrier went away.
* Drop DTR, abort any output in progress,
* indicate that output is not stopped, and
* send a hangup notification upstream.
*/
}
ZAS_BUSY);
}
}
}
}
if ((r0 & ZSRR0_BREAK) == 0) {
}
}
/*
* If a transmission has finished, indicate that it's
* finished, and start that line up again.
*/
else {
za->za_xmitblk = 0;
}
if (bp)
/* if we didn't start anything, then notify waiters */
} else {
}
/*
* A note about these overrun bits: all they do is *tell* someone
* about an error- They do not track multiple errors. In fact,
* you could consider them latched register bits if you like.
* We are only interested in printing the error message once for
* any cluster of overrun errrors.
*/
if (g_zsticks)
else
}
if (!hangup && do_ttycommon_qfull) {
}
za->za_hw_overrun = 0;
}
za->za_sw_overrun = 0;
}
if (unhangup)
(void) putnextctl(q, M_UNHANGUP);
if (m_break)
(void) putnextctl(q, M_BREAK);
while (head) {
if (!tail) {
break;
}
}
if (hangup) {
int flushflag;
/*
* If we're in the midst of close, then flush everything. Don't
* leave stale ioctls lying about.
*/
(void) putnextctl(q, M_HANGUP);
}
if (m_error)
za->za_soft_active = 0;
return (0);
}
/*
* Start output on a line, unless it's busy, frozen, or otherwise.
*/
static void
{
register int cc;
register queue_t *q;
/*
* If the chip is busy (i.e., we're waiting for a break timeout
* to expire, or for the current transmission to finish, or for
* output to finish draining from chip), don't grab anything new.
*/
return;
goto zsa_start_retransmit;
}
}
/*
* If we have a flow-control character to transmit, do it now.
*/
(ZSRR0_CTS|ZSRR0_TX_READY)) {
return;
}
} else if (!(SCC_READ0() & ZSRR0_TX_READY)) {
return;
}
ZSDELAY();
return;
}
/*
* If we're waiting for a delay timeout to expire, don't grab
* anything new.
*/
return;
return; /* not attached to a stream */
for (;;) {
return; /* no data to transmit */
/*
* We have a message block to work on.
* Check whether it's a break, a delay, or an ioctl (the latter
* occurs if the ioctl in question was waiting for the output
* to drain). If it's one of those, process it immediately.
*/
case M_BREAK:
/*
* Set the break bit, and arrange for "zsa_restart"
* to be called in 1/4 second; it will turn the
* break bit off, and call "zsa_start" to grab
* the next message.
*/
if (!za->za_zsa_restart_id) {
}
return; /* wait for this to finish */
case M_DELAY:
/*
* Arrange for "zsa_restart" to be called when the
* delay expires; it will turn MTS_DELAY off,
* and call "zsa_start" to grab the next message.
*/
if (! za->za_zsa_restart_id) {
zs,
}
return; /* wait for this to finish */
case M_IOCTL:
/*
* This ioctl was waiting for the output ahead of
* it to drain; obviously, it has. Do it, and
* then grab the next message after it.
*/
continue;
default: /* M_DATA */
goto zsa_start_transmit;
}
}
/*
* We have data to transmit. If output is stopped, put
* it back and try again later.
*/
return;
}
}
goto zsa_start_again;
}
/*
* In 5-bit mode, the high order bits are used
* to indicate character sizes less than five,
* so we need to explicitly mask before transmitting
*/
register unsigned char *p = rptr;
while (cnt--)
*p++ &= (unsigned char) 0x1f;
}
/*
* Set up this block for pseudo-DMA.
*/
(ZSRR0_CTS|ZSRR0_TX_READY)) {
return;
}
} else {
if (!(SCC_READ0() & ZSRR0_TX_READY)) {
return;
}
}
/*
* If the transmitter is ready, shove the first
* character out.
*/
ZSDELAY();
SCC_WRITEDATA(*rptr++);
#ifdef ZSA_DEBUG
#endif
}
/*
* Restart output on a line after a delay or break timer expired.
*/
static void
{
/*
* If break timer expired, turn off the break bit.
*/
if (!za->za_zsa_restart_id) {
return;
}
za->za_zsa_restart_id = 0;
}
}
/*
* See if the receiver has any data after zs_tick delay
*/
static void
{
queue_t *q;
int tmp;
int allocbcount = 0;
int do_ttycommon_qfull = 0;
return;
}
if (!q) {
return;
}
} else
if (g_zsticks)
else
if (za_soft_active || za_kick_active) {
return;
}
} else {
za->za_kick_rcv_id = 0;
}
for (;;) {
if (!mp)
break;
allocbcount++;
if (!(canputnext(q))) {
if (za->za_grace_flow_control >=
CRTSXOFF) {
allocbcount--;
break;
}
do_ttycommon_qfull = 1;
continue;
} else
} else
za->za_grace_flow_control = 0;
}
if (!head) {
} else {
if (!tail)
}
}
if (allocbcount)
if (do_ttycommon_qfull) {
}
while (head) {
if (!tail) {
break;
}
}
za->za_kick_active = 0;
}
/*
* Retry an "ioctl", now that "bufcall" claims we may be able to allocate
* the buffer we need.
*/
static void
{
queue_t *q;
/*
* The bufcall is no longer pending.
*/
if (!za->za_wbufcid) {
return;
}
za->za_wbufcid = 0;
return;
}
/*
* not pending any more
*/
}
}
/*
* Process an "ioctl" message sent down to us.
* Note that we don't need to get any locks until we are ready to access
* the hardware. Nothing we access until then is going to be altered
* outside of the STREAMS framework, so we should be safe.
*/
static void
{
register unsigned datasize;
int error;
/*
* We were holding an "ioctl" response pending the
* availability of an "mblk" to hold data to be passed up;
* another "ioctl" came through, which means that "ioctl"
* must have timed out or been aborted.
*/
}
/*
* The only way in which "ttycommon_ioctl" can fail is if the "ioctl"
* requires a response containing data to be returned to the user,
* and no mblk could be allocated for the data.
* No such "ioctl" alters our state. Thus, we always go ahead and
* do any state-changes the "ioctl" calls for. If we couldn't allocate
* the data, "ttycommon_ioctl" has stashed the "ioctl" away safely, so
* we just call "bufcall" to request that we be called back when we
* stand a better chance of allocating the data.
*/
else
if (datasize != 0) {
if (za->za_wbufcid)
return;
}
if (error == 0) {
/*
* "ttycommon_ioctl" did most of the work; we just use the
* data it set up.
*/
case TCSETS:
case TCSETSW:
case TCSETSF:
case TCSETA:
case TCSETAW:
case TCSETAF:
break;
}
} else if (error < 0) {
/*
* "ttycommon_ioctl" didn't do anything; we process it here.
*/
error = 0;
case TCSBRK:
if (error != 0)
break;
/*
* The delay ensures that a 3 byte transmit
* fifo is empty.
*/
/*
* Set the break bit, and arrange for
* "zsa_restart" to be called in 1/4 second;
* it will turn the break bit off, and call
* "zsa_start" to grab the next message.
*/
if (!za->za_zsa_restart_id) {
}
}
break;
case TIOCSBRK:
break;
case TIOCCBRK:
break;
case TIOCMSET:
case TIOCMBIS:
case TIOCMBIC: {
int mlines;
break;
}
if (error != 0)
break;
case TIOCMSET:
break;
case TIOCMBIS:
break;
case TIOCMBIC:
break;
}
break;
}
case TIOCMGET:
break;
}
else
/*
* qreply done below
*/
break;
default:
/*
* If we don't understand it, it's an error. NAK it.
*/
break;
}
}
if (error != 0) {
}
}
static int
{
register int b = 0;
b |= ZSRR0_CD;
b |= ZSRR0_CTS;
b |= ZSWR5_RTS;
b |= ZSWR5_DTR;
return (b);
}
static int
{
register int b;
b = 0;
b |= TIOCM_CAR;
b |= TIOCM_CTS;
b |= TIOCM_RTS;
b |= TIOCM_DTR;
return (b);
}
/*
* Assemble registers and flags necessary to program the port to our liking.
* For async operation, most of this is based on the values of
* the "c_iflag" and "c_cflag" fields supplied to us.
*/
static void
{
/*
* Hang up line.
*/
return;
}
/*
* set input speed same as output, as split speed not supported
*/
if (setibaud) {
} else {
}
}
/*
* disabled; doing that would mean you couldn't type an abort
* sequence.
*/
else
wr3 = 0;
wr4 = ZSWR4_X16_CLK;
/*
* XXX - should B134 set all this crap in the compatibility
* module, leaving this stuff fairly clean?
*/
wr3 |= ZSWR3_RX_6;
wr4 |= ZSWR4_1_5_STOP;
wr5 |= ZSWR5_TX_6;
} else {
case CS5:
wr3 |= ZSWR3_RX_5;
wr5 |= ZSWR5_TX_5;
break;
case CS6:
wr3 |= ZSWR3_RX_6;
wr5 |= ZSWR5_TX_6;
break;
case CS7:
wr3 |= ZSWR3_RX_7;
wr5 |= ZSWR5_TX_7;
break;
case CS8:
wr3 |= ZSWR3_RX_8;
wr5 |= ZSWR5_TX_8;
break;
}
/*
* The PARITY_SPECIAL bit causes a special rx
* interrupt on parity errors. Turn it on if
* we're checking the parity of characters.
*/
wr4 |= ZSWR4_PARITY_EVEN;
}
}
#if 0
/*
* The AUTO_CD_CTS flag enables the hardware flow control feature of
* the 8530, which allows the state of CTS and DCD to control the
* enabling of the transmitter and receiver, respectively. The
* receiver and transmitter still must have their enable bits set in
* WR3 and WR5, respectively, for CTS and DCD to be monitored this way.
* Hardware flow control can thus be implemented with no help from
* software.
*/
wr3 |= ZSWR3_AUTO_CD_CTS;
#endif
else
/*
* Here we assemble a set of changes to be passed to zs_program.
* Note: Write Register 15 must be set to enable BREAK and UNDERrun
* interrupts. It must also enable CD interrupts which, although
* not processed by the hardware interrupt handler, will be processed
* by zsa_process, indirectly resulting in a SIGHUP being delivered
* to the controlling process if CD drops. CTS interrupts must NOT
* systems at boot time if synchronous modems that supply transmit
* clock are attached to any of their serial ports.
*/
!(flags & ZSP_PARITY_SPECIAL)) ||
(flags & ZSP_PARITY_SPECIAL)) ||
}
}
/*
* Get the current speed of the console and turn it into something
* UNIX knows about - used to preserve console speed when UNIX comes up.
*/
int
{
return (uspeed);
/*
* 9600 baud if we can't figure it out
*/
return (ISPEED);
}
/*
* callback routine when enough memory is available.
*/
static void
{
}
}
/*
* Set the receiver flags
*/
static void
{
case CS5:
mask = 0x1f;
break;
case CS6:
mask = 0x3f;
break;
case CS7:
mask = 0x7f;
break;
default:
mask = 0xff;
}
} else
} else
}
static int
{
queue_t *q;
if (zs->zs_suspended) {
return (DDI_SUCCESS);
}
/*
* Turn off interrupts and get any bytes in receiver
*/
za->za_zsa_restart_id = 0;
za->za_kick_rcv_id = 0;
/*
* Cancel any timeouts
*/
if (restart_id)
(void) untimeout(restart_id);
if (kick_rcv_id)
(void) untimeout(kick_rcv_id);
/*
* Since we have turned off interrupts, zsa_txint will not be called
* and no new chars will given to the chip. We just wait for the
* current character(s) to drain.
*/
/*
* Return remains of partially sent message to queue
*/
}
if (bp)
}
/*
* Stop any breaks in progress.
*/
}
/*
* Now get a copy of current registers setting.
*/
/*
* We do this on the off chance that zsa_close is waiting on a timed
* break to complete and nothing else.
*/
return (DDI_SUCCESS);
}
static int
{
if (!(zs->zs_suspended)) {
return (DDI_SUCCESS);
}
/*
* Restore H/W state
*/
/*
* Enable all interrupts for this chip and delay to let chip settle
*/
DELAY(4000);
/*
* Restart receiving and transmitting
*/
zs->zs_suspended = 0;
return (DDI_SUCCESS);
}
#ifdef ZSA_DEBUG
static void
{
printf("tflag:\n");
printf("\n");
printf("\n");
}
#endif
/*
* Check for abort character sequence
*/
static boolean_t
{
static int state = 0;
state = 0;
return (B_TRUE);
}
} else {
}
return (B_FALSE);
}