e0cc03a09b
kqueue provides a stateful and efficient event notification framework currently supported events include socket, file, directory, fifo, pipe, tty and device changes, and monitoring of processes and signals kqueue is supported by all writable filesystems in NetBSD tree (with exception of Coda) and all device drivers supporting poll(2) based on work done by Jonathan Lemon for FreeBSD initial NetBSD port done by Luke Mewburn and Jason Thorpe
694 lines
15 KiB
C
694 lines
15 KiB
C
/* $NetBSD: a12dc.c,v 1.10 2002/10/23 09:10:28 jdolecek Exp $ */
|
|
|
|
/* [Notice revision 2.2]
|
|
* Copyright (c) 1997, 1998 Avalon Computer Systems, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Author: Ross Harvey
|
|
*
|
|
* 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 and
|
|
* author 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 Avalon Computer Systems, Inc. nor the names of
|
|
* its contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
* 4. This copyright will be assigned to The NetBSD Foundation on
|
|
* 1/1/2000 unless these terms (including possibly the assignment
|
|
* date) are updated in writing by Avalon prior to the latest specified
|
|
* assignment date.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY AVALON COMPUTER SYSTEMS, 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 AVALON OR THE 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.
|
|
*/
|
|
|
|
/*
|
|
* The A12 uses what DEC calls a "detached console", i.e., some of the console
|
|
* implementation is on a dedicated processor with its own RAM.
|
|
*
|
|
* The A12 Detached Console interface uses two 16 bit registers (per CPU), one
|
|
* going from the CPU to the a12ctrl processor and one going back the other
|
|
* way. The first is polled, the second produces a GInt.
|
|
*
|
|
* In the very early days we loaded program images through this interface.
|
|
*
|
|
* Consequently, it developed an overly complicated (but sort of fast)
|
|
* inverting sync/ack that isn't needed at all for its present application as
|
|
* a text console device.
|
|
*
|
|
* One possible solution: most of the channels are undefined, so a console
|
|
* channel using a stateless ack could be defined, with corresponding changes
|
|
* to the backplane 68360 code.
|
|
*
|
|
* This file is complicated somewhat by its use in three different kernels:
|
|
* NetBSD, the A12 CPU-resident console, and the a12ctrl backplane processor.
|
|
* (The protocol is symmetrical.)
|
|
*/
|
|
|
|
#include "opt_avalon_a12.h" /* Config options headers */
|
|
#include "opt_kgdb.h"
|
|
|
|
#ifndef BSIDE
|
|
#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */
|
|
|
|
__KERNEL_RCSID(0, "$NetBSD: a12dc.c,v 1.10 2002/10/23 09:10:28 jdolecek Exp $");
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/tty.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/user.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/device.h>
|
|
#include <sys/conf.h>
|
|
|
|
#include <dev/cons.h>
|
|
|
|
#include <machine/cpuconf.h>
|
|
#include <machine/autoconf.h>
|
|
#include <machine/rpb.h>
|
|
|
|
#include <alpha/pci/a12creg.h>
|
|
#include <alpha/pci/a12cvar.h>
|
|
#include <alpha/pci/pci_a12.h>
|
|
|
|
#include "a12dcreg.h"
|
|
|
|
#define A12DC() /* Generate ctags(1) key */
|
|
|
|
#define MAX_MODULES 1
|
|
|
|
int a12dcmatch __P((struct device *, struct cfdata *, void *));
|
|
void a12dcattach __P((struct device *, struct device *, void *));
|
|
|
|
struct a12dc_softc {
|
|
struct device sc_dev;
|
|
} a12dc_softc;
|
|
|
|
CFATTACH_DECL(a12dc, sizeof(struct a12dc_softc),
|
|
a12dcmatch, a12dcattach, NULL, NULL);
|
|
|
|
extern struct cfdriver a12dc_cd;
|
|
|
|
dev_type_open(a12dcopen);
|
|
dev_type_close(a12dcclose);
|
|
dev_type_read(a12dcread);
|
|
dev_type_write(a12dcwrite);
|
|
dev_type_ioctl(a12dcioctl);
|
|
dev_type_stop(a12dcstop);
|
|
dev_type_tty(a12dctty);
|
|
dev_type_poll(a12dcpoll);
|
|
|
|
const struct cdevsw a12dc_cdevsw = {
|
|
a12dcopen, a12dcclose, a12dcread, a12dcwrite, a12dcioctl,
|
|
a12dcstop, a12dctty, a12dcpoll, nommap, ttykqfilter, D_TTY
|
|
};
|
|
|
|
int a12dcfound; /* There Can Be Only One. */
|
|
|
|
struct a12dc_config { int im_not_used; } a12dc_configuration;
|
|
|
|
static struct tty *a12dc_tty[1];
|
|
|
|
void a12dcstart __P((struct tty *));
|
|
void a12dctimeout __P((void *));
|
|
int a12dcparam __P((struct tty *, struct termios *));
|
|
void a12dc_init(struct a12dc_config *, int);
|
|
static void a12cdrputc __P((int));
|
|
int a12dccngetc(dev_t);
|
|
void a12dccnputc(dev_t, int);
|
|
void a12dccnpollc(dev_t, int);
|
|
/* static int get_bc_char(int mn, int chan); */
|
|
/* static int get_bc_any(int,int *,int *); */
|
|
int a12dcintr __P((void *));
|
|
static void a12_worry(int worry_number);
|
|
static void A12InitBackDriver(int);
|
|
|
|
int
|
|
a12dcmatch(parent, match, aux)
|
|
struct device *parent;
|
|
struct cfdata *match;
|
|
void *aux;
|
|
{
|
|
struct pcibus_attach_args *pba = aux;
|
|
|
|
return cputype == ST_AVALON_A12
|
|
&& strcmp(pba->pba_busname, a12dc_cd.cd_name) == 0
|
|
&& !a12dcfound;
|
|
}
|
|
|
|
void
|
|
a12dcattach(parent, self, aux)
|
|
struct device *parent, *self;
|
|
void *aux;
|
|
{
|
|
struct tty *tp;
|
|
struct a12dc_config *ccp;
|
|
|
|
/* note that we've attached the chipset; can't have 2 A12Cs. */
|
|
a12dcfound = 1;
|
|
|
|
printf(": driver %s\n", "$Revision: 1.10 $");
|
|
|
|
tp = a12dc_tty[0] = ttymalloc();
|
|
tp->t_oproc = a12dcstart;
|
|
tp->t_param = a12dcparam;
|
|
tty_attach(tp);
|
|
|
|
ccp = &a12dc_configuration;
|
|
a12dc_init(ccp, 1);
|
|
}
|
|
|
|
void
|
|
a12dc_init(ccp, mallocsafe)
|
|
struct a12dc_config *ccp;
|
|
int mallocsafe;
|
|
{
|
|
}
|
|
|
|
/*
|
|
* XXX definitions specific to only one of the kernels will be moved into
|
|
* kernel-local include files. (One of these days.)
|
|
*/
|
|
#define spla12dc() spltty()
|
|
#define a12yield()
|
|
#define CDRADDR(m) ((mmreg_t *)(REGADDR(A12_CDR))) /* ignore m */
|
|
#define modenormal()
|
|
#define mb() alpha_mb()
|
|
#define wmb() alpha_wmb()
|
|
|
|
#else
|
|
|
|
#include "product.def"
|
|
#include "ghs.h"
|
|
#include "a12ctrl.h"
|
|
|
|
#define wmb()
|
|
#define mb()
|
|
#define swpipl(a) (a)
|
|
#define splx(a) (a)
|
|
#define spla12dc()
|
|
#define hrhpanic(s,v) panic((s))
|
|
#define CDRADDR(m) ((mmreg_t *)(A12_BACKPLANE+(m)))
|
|
|
|
#endif
|
|
|
|
static int msgetput __P((register mstate_type *, int, int));
|
|
static void checkinit __P((void));
|
|
|
|
static void
|
|
A12InitBackDriver(int m)
|
|
{
|
|
/*
|
|
* XXX memset() would be good here, but there is a (temporary) reason
|
|
* for all this right now
|
|
*/
|
|
a12_mstate[m].xcdr = CDRADDR(m);
|
|
a12_mstate[m].txrdy_out = A12C_TXRDY;
|
|
a12_mstate[m].txrdy_in = A12C_TXRDY;
|
|
a12_mstate[m].txack_out = A12C_TXACK;
|
|
a12_mstate[m].txack_in = A12C_TXACK;
|
|
a12_mstate[m].txsv = tx_idle;
|
|
a12_mstate[m].rxsv = rx_idle;
|
|
a12_mstate[m].reset_scanner= 1;
|
|
a12_mstate[m].reset_loader = 1;
|
|
a12_mstate[m].up = 1;
|
|
a12_mstate[m].lastr = 0; /* last char. received */
|
|
a12_mstate[m].lastrc = 0; /* last received channel */
|
|
a12_mstate[m].rx_busy_wait = 0; /* last received busy ticks */
|
|
a12_mstate[m].rx_busy_cnt = 0; /* last received start clock */
|
|
a12_mstate[m].lastt = 0; /* last char. trasmitted */
|
|
a12_mstate[m].lasttc = 0; /* last transmit channel */
|
|
a12_mstate[m].tx_busy_wait = 0; /* last transmitted busy ticks */
|
|
a12_mstate[m].tx_busy_cnt = 0; /* last transmitted start clock */
|
|
a12_mstate[m].tbytes = 0;
|
|
a12_mstate[m].tblkbytes = 0;
|
|
a12_mstate[m].tblks = 0;
|
|
a12_mstate[m].blwobe = 0;
|
|
a12_mstate[m].blwbe = 0;
|
|
a12_mstate[m].max_blkretry = 0;
|
|
a12_mstate[m].max_blktime = 0;
|
|
a12_mstate[m].max_blktimesz = 0;
|
|
a12_mstate[m].avg_blksz = 0;
|
|
a12_mstate[m].avg_blktime = 0;
|
|
a12_mstate[m].retry_time = 0;
|
|
}
|
|
|
|
static void
|
|
checkinit()
|
|
{
|
|
static int did_init;
|
|
|
|
if (!did_init) {
|
|
A12InitBackDriver(0);
|
|
did_init = 1;
|
|
}
|
|
}
|
|
|
|
int
|
|
a12dcopen(dev, flag, mode, p)
|
|
dev_t dev;
|
|
int flag, mode;
|
|
struct proc *p;
|
|
{
|
|
int unit = minor(dev);
|
|
struct tty *tp;
|
|
int s;
|
|
|
|
if (unit >= 1)
|
|
return ENXIO;
|
|
|
|
#ifdef KGDB
|
|
if (flags & COM_HW_KGDB)
|
|
return EBUSY;
|
|
#endif
|
|
|
|
if (!a12dc_tty[unit]) {
|
|
tp = a12dc_tty[unit] = ttymalloc();
|
|
tty_attach(tp);
|
|
} else
|
|
tp = a12dc_tty[unit];
|
|
|
|
if ((tp->t_state & TS_ISOPEN) &&
|
|
(tp->t_state & TS_XCLUDE) &&
|
|
p->p_ucred->cr_uid != 0)
|
|
return EBUSY;
|
|
|
|
s = spltty();
|
|
|
|
tp->t_oproc = a12dcstart;
|
|
tp->t_param = a12dcparam;
|
|
tp->t_dev = dev;
|
|
if ((tp->t_state & TS_ISOPEN) == 0) {
|
|
tp->t_state |= TS_CARR_ON;
|
|
ttychars(tp);
|
|
tp->t_iflag = TTYDEF_IFLAG;
|
|
tp->t_oflag = TTYDEF_OFLAG;
|
|
tp->t_cflag = TTYDEF_CFLAG|CLOCAL;
|
|
tp->t_lflag = TTYDEF_LFLAG;
|
|
tp->t_ispeed = tp->t_ospeed = 9600;
|
|
ttsetwater(tp);
|
|
/* XXX XXX XXX
|
|
a12_intr_register_icw(a12dcintr);
|
|
*/
|
|
} else if (tp->t_state&TS_XCLUDE && p->p_ucred->cr_uid != 0) {
|
|
splx(s);
|
|
return EBUSY;
|
|
}
|
|
|
|
splx(s);
|
|
|
|
return (*tp->t_linesw->l_open)(dev, tp);
|
|
}
|
|
|
|
int
|
|
a12dcclose(dev, flag, mode, p)
|
|
dev_t dev;
|
|
int flag, mode;
|
|
struct proc *p;
|
|
{
|
|
int unit = minor(dev);
|
|
struct tty *tp = a12dc_tty[unit];
|
|
|
|
(*tp->t_linesw->l_close)(tp, flag);
|
|
ttyclose(tp);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
a12dcread(dev, uio, flag)
|
|
dev_t dev;
|
|
struct uio *uio;
|
|
int flag;
|
|
{
|
|
struct tty *tp = a12dc_tty[minor(dev)];
|
|
|
|
return ((*tp->t_linesw->l_read)(tp, uio, flag));
|
|
}
|
|
|
|
int
|
|
a12dcwrite(dev, uio, flag)
|
|
dev_t dev;
|
|
struct uio *uio;
|
|
int flag;
|
|
{
|
|
struct tty *tp = a12dc_tty[minor(dev)];
|
|
|
|
return ((*tp->t_linesw->l_write)(tp, uio, flag));
|
|
}
|
|
|
|
int
|
|
a12dcpoll(dev, events, p)
|
|
dev_t dev;
|
|
int events;
|
|
struct proc *p;
|
|
{
|
|
struct tty *tp = a12dc_tty[minor(dev)];
|
|
|
|
return ((*tp->t_linesw->l_poll)(tp, events, p));
|
|
}
|
|
|
|
int
|
|
a12dcioctl(dev, cmd, data, flag, p)
|
|
dev_t dev;
|
|
u_long cmd;
|
|
caddr_t data;
|
|
int flag;
|
|
struct proc *p;
|
|
{
|
|
int unit = minor(dev);
|
|
struct tty *tp = a12dc_tty[unit];
|
|
int error;
|
|
|
|
error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, p);
|
|
if (error != EPASSTHROUGH)
|
|
return error;
|
|
return ttioctl(tp, cmd, data, flag, p);
|
|
}
|
|
|
|
int
|
|
a12dcparam(tp, t)
|
|
struct tty *tp;
|
|
struct termios *t;
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
a12dcstart(tp)
|
|
struct tty *tp;
|
|
{
|
|
int s;
|
|
|
|
s = spltty();
|
|
if (tp->t_state & (TS_TTSTOP | TS_BUSY))
|
|
goto out;
|
|
if (tp->t_outq.c_cc <= tp->t_lowat) {
|
|
if (tp->t_state & TS_ASLEEP) {
|
|
tp->t_state &= ~TS_ASLEEP;
|
|
wakeup((caddr_t)&tp->t_outq);
|
|
}
|
|
selwakeup(&tp->t_wsel);
|
|
}
|
|
tp->t_state |= TS_BUSY;
|
|
while (tp->t_outq.c_cc != 0)
|
|
a12dccnputc(tp->t_dev, getc(&tp->t_outq));
|
|
tp->t_state &= ~TS_BUSY;
|
|
out:
|
|
splx(s);
|
|
}
|
|
|
|
/*
|
|
* Stop output on a line.
|
|
*/
|
|
void
|
|
a12dcstop(tp, flag)
|
|
struct tty *tp;
|
|
{
|
|
int s;
|
|
|
|
s = spltty();
|
|
if (tp->t_state & TS_BUSY)
|
|
if ((tp->t_state & TS_TTSTOP) == 0)
|
|
tp->t_state |= TS_FLUSH;
|
|
splx(s);
|
|
}
|
|
|
|
int
|
|
a12dcintr(v)
|
|
void *v;
|
|
{
|
|
#if 1
|
|
DIE();
|
|
#else
|
|
struct tty *tp = v;
|
|
u_char c;
|
|
|
|
while (a12dccnlookc(tp->t_dev, &c)) {
|
|
if (tp->t_state & TS_ISOPEN)
|
|
(*tp->t_linesw->l_rint)(c, tp);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
struct tty *
|
|
a12dctty(dev)
|
|
dev_t dev;
|
|
{
|
|
|
|
if (minor(dev) != 0)
|
|
panic("a12dctty: bogus");
|
|
|
|
return a12dc_tty[0];
|
|
}
|
|
|
|
int
|
|
a12dccnattach()
|
|
{
|
|
static struct consdev a12dccons = {
|
|
NULL, NULL, a12dccngetc, a12dccnputc, a12dccnpollc, NULL,
|
|
NODEV, CN_NORMAL
|
|
};
|
|
|
|
a12dccons.cn_dev = makedev(cdevsw_lookup_major(&a12dc_cdevsw), 0);
|
|
|
|
cn_tab = &a12dccons;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
a12dccngetc(dev)
|
|
dev_t dev;
|
|
{
|
|
for(;;)
|
|
continue;
|
|
}
|
|
|
|
void
|
|
a12dccnputc(dev, c)
|
|
dev_t dev;
|
|
int c;
|
|
{
|
|
a12cdrputc(c);
|
|
}
|
|
|
|
void
|
|
a12dccnpollc(dev, on)
|
|
dev_t dev;
|
|
int on;
|
|
{
|
|
}
|
|
|
|
static void
|
|
a12cdrputc(int c)
|
|
{
|
|
int s = spla12dc();
|
|
|
|
checkinit();
|
|
while(msgetput(MSP(0),CHANNEL_MONITOR,c)) {
|
|
/*if(check_cdr_ok)
|
|
check_cdr();*/
|
|
DELAY(100);
|
|
}
|
|
splx(s);
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
check_cdr()
|
|
{
|
|
int bpchar,cnumber;
|
|
static int srom_word_address,
|
|
kmsg;
|
|
int push_check_ok;
|
|
int ipl;
|
|
|
|
if(!get_bc_any(0,&bpchar,&cnumber))
|
|
return;
|
|
switch(cnumber) {
|
|
default:
|
|
printf("Unknown cdr channel %d",cnumber);
|
|
break;
|
|
case CHANNEL_KDATA:
|
|
if(!kmsg)
|
|
printf("Ignoring new kernel\n");;
|
|
kmsg = 1;
|
|
break;
|
|
case CHANNEL_KMARK:
|
|
kmsg = 0;
|
|
break;
|
|
#ifndef _KERNEL /* NetBSD kernel, that is. "if defined: rtmon kernel" */
|
|
case CHANNEL_SROM_A:
|
|
srom_word_address = bpchar;
|
|
break;
|
|
case CHANNEL_SROM_D:
|
|
push_check_ok = check_cdr_ok;
|
|
check_cdr_ok = 0;
|
|
ipl = swpipl(7);
|
|
write_ethernet_srom(srom_word_address,bpchar);
|
|
++srom_word_address;
|
|
(void)swpipl(ipl);
|
|
check_cdr_ok = push_check_ok;
|
|
break;
|
|
|
|
case CHANNEL_MULTI:
|
|
logmchar(bpchar,"<");
|
|
SerialByteReceived(bpchar);
|
|
break;
|
|
case CHANNEL_MONITOR:
|
|
if (bpchar == CPX_PANIC)
|
|
/* TJF - Could kill all processes and then panic? */
|
|
hrhpanic("Panic in cooperating CPU.",0);
|
|
else cpxchar(bpchar);
|
|
break;
|
|
#endif
|
|
case CHANNEL_CONSOLE:
|
|
#ifdef A12CON /* XXX this can be done much better */
|
|
virtual_keyboard_byte(bpchar);
|
|
#else
|
|
ipl7putc(bpchar);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
static int
|
|
get_bc_any(int mn, int *c, int *chan) {
|
|
mstate_type *ms = &a12_mstate[mn];
|
|
|
|
(void)mcgetput(mn, 0, -1);
|
|
if(ms->rxsv==rx_busy) {
|
|
*c = ms->c2b_char & 0xff;
|
|
*chan = ms->c2b_channel;
|
|
ms->rxsv = rx_idle;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
get_bc_char(int mn, int chan) {
|
|
int newc,newchan;
|
|
|
|
if(get_bc_any(mn,&newc,&newchan)) {
|
|
if(newchan!=chan)
|
|
printf("receiver busy on dumb channel %d", newchan);
|
|
return newc;
|
|
}
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
msgetput(register mstate_type *ms, int channel, int c)
|
|
{
|
|
int i,t;
|
|
int ipl;
|
|
|
|
if(c==-2 || ms->up==0) {
|
|
if(c!=-2)
|
|
a12_worry(10);
|
|
ms->txsv = tx_idle;
|
|
ms->rxsv = rx_idle;
|
|
ms->cdr = 0;
|
|
return 0;
|
|
}
|
|
if(!(0<=channel && channel<64)) {
|
|
a12_worry(7);
|
|
return 0;
|
|
}
|
|
ipl = spla12dc();
|
|
for(i=0; i<20; ++i) {
|
|
switch(ms->txsv) {
|
|
case tx_idle:
|
|
if(c!=-1) {
|
|
ms->lastt = c;
|
|
ms->lasttc = channel;
|
|
ms->cdr = (ms->cdr & (A12C_TXACK | A12C_TXRDY))
|
|
| channel<<8
|
|
| c;
|
|
*ms->xcdr = ms->cdr;
|
|
wmb();
|
|
ms->cdr = ((ms->cdr & A12C_TXACK) &~ A12C_TXRDY)
|
|
| ms->txrdy_out
|
|
| channel<<8
|
|
| c;
|
|
*ms->xcdr = ms->cdr;
|
|
wmb();
|
|
ms->txsv = tx_braaw;
|
|
c = -1;
|
|
continue;
|
|
}
|
|
break;
|
|
case tx_braaw:
|
|
mb();
|
|
if((*ms->xcdr & A12C_TXACK)==ms->txack_in) {
|
|
ms->txrdy_out ^= A12C_TXRDY;
|
|
ms->txack_in ^= A12C_TXACK;
|
|
ms->txsv = tx_idle;
|
|
ms->tbytes++;
|
|
continue;
|
|
}
|
|
break;
|
|
default:
|
|
(void)splx(ipl);
|
|
a12_worry(5);
|
|
return 0;
|
|
}
|
|
switch(ms->rxsv) {
|
|
case rx_idle:
|
|
mb();
|
|
t = *ms->xcdr;
|
|
if((t & A12C_TXRDY)==ms->txrdy_in) {
|
|
ms->cdr = (ms->cdr & ~A12C_TXACK)
|
|
| ms->txack_out;
|
|
*ms->xcdr = ms->cdr;
|
|
wmb();
|
|
ms->c2b_char = t & 0xff;
|
|
ms->lastr = ms->c2b_char;
|
|
ms->c2b_channel = (t & CHMASK) >> 8;
|
|
ms->lastrc = ms->c2b_channel;
|
|
ms->txrdy_in ^= A12C_TXRDY;
|
|
ms->txack_out^= A12C_TXACK;
|
|
ms->rxsv = rx_busy;
|
|
continue;
|
|
}
|
|
break;
|
|
case rx_busy:
|
|
break;
|
|
default:
|
|
(void)splx(ipl);
|
|
a12_worry(6);
|
|
return 0;
|
|
}
|
|
if(c==-1)
|
|
break;
|
|
}
|
|
(void)splx(ipl);
|
|
return c!=-1;
|
|
}
|
|
|
|
static void
|
|
a12_worry(int worry_number)
|
|
{
|
|
a12console_last_unexpected_error = worry_number;
|
|
}
|