Files
Arquivotheca.Solaris-2.5/uts/sun/io/mcpp.c
seta75D 7c4988eac0 Init
2021-10-11 19:38:01 -03:00

1207 lines
25 KiB
C
Executable File

/*
* Copyright (c) 1992 by Sun Microsystems, Inc.
*/
#ident "@(#)mcpp.c 1.19 95/08/02 SMI"
/*
* Sun MCP Parallel Port Driver
*
* Handles the parallel function for the ALM-2 card.
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/systm.h>
#include <sys/sysmacros.h>
#include <sys/signal.h>
#include <sys/kmem.h>
#include <sys/termios.h>
#include <sys/termio.h>
#include <sys/strsubr.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/tty.h>
#include <sys/ptyvar.h>
#include <sys/cred.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/buf.h>
#include <sys/reboot.h>
#include <sys/mkdev.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>
#include <sys/modctl.h>
#include <sys/strtty.h>
#include <sys/suntty.h>
#include <sys/ksynch.h>
#include <sys/mcpzsdev.h>
#include <sys/mcpio.h>
#include <sys/mcpreg.h>
#include <sys/mcpcom.h>
#include <sys/mcpvar.h>
#include <sys/consdev.h>
#include <sys/ser_async.h>
#include <sys/debug/debug.h>
#include <sys/conf.h>
#include <sys/promif.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
/*
* Define some local marros.
*/
#ifdef DEBUG
#define MCPP_DEBUG 0
#endif
#ifdef DEBUG
static int mcpp_debug = MCPP_DEBUG;
#define DEBUGF(level, args) \
if (mcpp_debug >= (level)) cmn_err args;
#else
#define DEBUGF(level, args) /* Nothing */
#endif /* !MCP_DEBUG */
/*
* External variables and functions.
*/
extern kcondvar_t lbolt_cv;
/*
* Define some local variables.
*/
typedef struct mcpp_state {
mcp_iface_t *iface;
dev_info_t *dip;
} mcpp_soft_t;
static void *mcpp_soft_p;
/*
* Local Function Prototypes.
*/
static int mcpp_identify(dev_info_t *);
static int mcpp_probe(dev_info_t *);
static int mcpp_attach(dev_info_t *, ddi_attach_cmd_t);
static int mcpp_detach(dev_info_t *, ddi_detach_cmd_t);
static int mcpp_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int mcpp_txend(mcpcom_t *);
static void mcpp_start(mcpaline_t *);
static void mcpp_restart(mcpaline_t *);
static void mcpp_copyin(queue_t *q, mblk_t *, caddr_t, uint_t);
static void mcpp_copyout(queue_t *q, mblk_t *, caddr_t, uint_t);
static int mcpp_open(queue_t *, dev_t *, int, int, cred_t *);
static int mcpp_close(queue_t *, int, cred_t *);
static int mcpp_wput(queue_t *, mblk_t *);
static void mcpp_cntl(mcpaline_t *, int, unsigned char *);
static int mcpp_pe(mcpcom_t *);
static int mcpp_slct(mcpcom_t *);
static int mcpp_ioctl(mcpaline_t *za, queue_t *q, mblk_t *mp);
/*
* Define some protocol operations.
*/
/* Parallel Port MCP Ops vector */
static struct mcpops mcpp_ops = {
0, /* Xmit buffer empty. */
0, /* External Status */
0, /* Receive Char Avail */
0, /* Special Receive Cond. */
mcpp_txend, /* Transmit DMA Done. */
0, /* Receive DMA done. */
0, /* Fifo recv char avail. */
mcpp_pe, /* PE: printer out of paper */
mcpp_slct, /* SLCT: printer is on line. */
};
/*
* Declare some streams structures.
*/
static struct module_info mcp_info = {
0x4d44, /* Module ID */
"mcpp", /* Module name */
0, /* Min packet size. */
INFPSZ, /* Max packet size. */
2048, /* Hi Water */
128 /* Lo Water */
};
static struct qinit mcpp_rinit = {
putq, /* Put Proc. */
NULL, /* Service Proc. */
mcpp_open, /* Open */
mcpp_close, /* Close */
NULL, /* Admin */
&mcp_info, /* Module Info Struct. */
NULL /* Module Stat Struct. */
};
static struct qinit mcpp_winit = {
mcpp_wput, /* Put Proc. */
NULL, /* Service Proc. */
NULL, /* Open */
NULL, /* Close */
NULL, /* Admin */
&mcp_info, /* Module Info State. */
NULL
};
/* MCP Stream tab for SCC's */
struct streamtab mcppstab = {
&mcpp_rinit,
&mcpp_winit,
NULL,
NULL,
};
/*
* Define mcpp cb_ops and dev_ops.
*/
static struct cb_ops mcpp_cb_ops = {
nodev, /* Open */
nodev, /* Close. */
nodev, /* Strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
nodev, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* Poll. */
ddi_prop_op, /* Prop Op. */
&mcppstab, /* streamtab. */
D_NEW | D_MP,
};
static struct dev_ops mcpp_dev_ops = {
DEVO_REV, /* Devo_rev */
0, /* Refcnt */
mcpp_getinfo, /* get_dev_info */
mcpp_identify, /* identify */
mcpp_probe, /* probe. */
mcpp_attach, /* attach */
mcpp_detach, /* detach */
nodev, /* reset */
&mcpp_cb_ops, /* Driver ops */
(struct bus_ops *)0, /* Bus ops. */
};
/*
* Module linkage information for the kernel
*/
static struct modldrv modldrv = {
&mod_driverops, /* Type of Module = Driver */
"MCP Parallel Port driver v1.19", /* Driver Identifier string. */
&mcpp_dev_ops, /* Driver Ops. */
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modldrv, NULL
};
/*
* Module initialization
*/
int
_init(void)
{
int stat;
DEBUGF(1, (CE_CONT, "_init: entering ... \n"));
stat = ddi_soft_state_init(&mcpp_soft_p, sizeof (mcpp_soft_t), N_MCP);
if (stat != DDI_SUCCESS)
return (stat);
stat = mod_install(&modlinkage);
if (stat != 0) {
ddi_soft_state_fini(&mcpp_soft_p);
}
return (stat);
}
int
_info(struct modinfo *infop)
{
return (mod_info(&modlinkage, infop));
}
int
_fini()
{
int stat;
DEBUGF(1, (CE_CONT, "_fini: entering ... \n"));
if ((stat = mod_remove(&modlinkage)) != DDI_SUCCESS)
return (stat);
ddi_soft_state_fini(&mcpp_soft_p);
DEBUGF(1, (CE_CONT, "_fini: exiting ... \n"));
return (DDI_SUCCESS);
}
/*
* Auto configuration routines.
*/
static int
mcpp_identify(dev_info_t *dip)
{
DEBUGF(1, (CE_CONT, "mcpp_indentify: ddi_get_name(%s)\n",
ddi_get_name(dip)));
if (strcmp(ddi_get_name(dip), "mcpp") == 0)
return (DDI_IDENTIFIED);
else
return (DDI_NOT_IDENTIFIED);
}
/*ARGSUSED*/
static int
mcpp_probe(dev_info_t *dip)
{
return (DDI_PROBE_SUCCESS);
}
static int
mcpp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
mcp_state_t *softp;
mcpp_soft_t *mcpp;
mcpcom_t *zs;
mcpaline_t *za;
mcp_iface_t *iface;
mcp_dev_t *devp;
int instance;
char name[80];
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
DEBUGF(1, (CE_CONT, "mcpp_attach%d: entering ...\n",
ddi_get_instance(dip)));
/* Get pointer fo the interface from the parent. */
if ((iface = (mcp_iface_t *)ddi_get_driver_private(dip)) == NULL) {
cmn_err(CE_CONT, "mcpp%d: parent failed initialize interface\n",
ddi_get_instance(dip));
return (DDI_FAILURE);
}
instance = ddi_get_instance(dip);
if (ddi_soft_state_zalloc(mcpp_soft_p, instance) != DDI_SUCCESS)
return (DDI_FAILURE);
mcpp = (mcpp_soft_t *)ddi_get_soft_state(mcpp_soft_p, instance);
mcpp->iface = iface;
mcpp->dip = dip;
softp = (mcp_state_t *)iface->priv;
devp = softp->devp;
zs = &softp->mcpcom[N_MCP_ZSDEVS];
za = &softp->mcpaline[N_MCP_ZSDEVS];
sprintf(name, "%d", instance);
if (ddi_create_minor_node(dip, name, S_IFCHR, instance,
"ddi_parallel", NULL) != DDI_SUCCESS) {
ddi_soft_state_free(mcpp_soft_p, instance);
return (DDI_FAILURE);
}
/*
* Initialize locks.
*/
za->za_flags_cv = kmem_zalloc(sizeof (kcondvar_t), KM_SLEEP);
sprintf(name, "mcpp-lock%d", instance);
mutex_init(&zs->zs_excl, name, MUTEX_DRIVER,
(void *)softp->iblk_cookie);
sprintf(name, "mcpp-lock-hi%d", instance);
mutex_init(&zs->zs_excl_hi, name, MUTEX_DRIVER,
(void *)softp->iblk_cookie);
sprintf(name, "mcpp-cv%d", instance);
cv_init(za->za_flags_cv, name, CV_DRIVER, (void *)softp->iblk_cookie);
/*
* Now initialize the rest of the device.
*/
mutex_enter(&zs->zs_excl);
mutex_enter(&zs->zs_excl_hi);
zs->zs_unit = N_MCP_ZSDEVS;
zs->mc_unit = instance;
zs->zs_flags = 0;
zs->zs_rerror = 0;
zs->zs_state = (caddr_t)softp;
zs->mcp_addr = devp;
zs->mcp_txdma = MCP_DMA_GETCHAN(iface, (caddr_t)softp, 0,
TX_DIR, PR_DMA);
zs->mcp_rxdma = MCP_DMA_GETCHAN(iface, (caddr_t)softp, 0,
RX_DIR, PR_DMA);
if (softp->mcpsoftCAR & 0x10000)
zs->zs_flags = MCPRIGNSLCT;
else
zs->zs_flags = 0;
za->za_dmabuf = zs->mcp_addr->printer_buf;
za->za_common = (struct zscom *)zs;
zs->zs_priv = (caddr_t)za;
mutex_exit(&zs->zs_excl_hi);
mutex_exit(&zs->zs_excl);
DEBUGF(1, (CE_CONT, "mcpp_attach%d: exiting ... \n", instance));
ddi_report_dev(dip);
return (DDI_SUCCESS);
}
static int
mcpp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
mcpp_soft_t *mcpp;
mcp_state_t *softp;
mcpcom_t *zs;
mcpaline_t *za;
char name[80];
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
DEBUGF(1, (CE_CONT, "mcpp_detach%d: entering ... \n",
ddi_get_instance(dip)));
mcpp = (mcpp_soft_t *)ddi_get_soft_state(mcpp_soft_p,
ddi_get_instance(dip));
softp = (mcp_state_t *)mcpp->iface->priv;
zs = &(softp->mcpcom[N_MCP_ZSDEVS]);
za = (mcpaline_t *)zs->zs_priv;
mutex_enter(&zs->zs_excl);
mutex_enter(&zs->zs_excl_hi);
/* Is the device still open ?? */
if (za->za_flags & (ZAS_ISOPEN | ZAS_WOPEN)) {
mutex_exit(&zs->zs_excl_hi);
mutex_exit(&zs->zs_excl);
return (DDI_FAILURE);
}
za->za_flags = ZAS_REFUSE;
mutex_exit(&zs->zs_excl_hi);
mutex_exit(&zs->zs_excl);
cv_destroy(za->za_flags_cv);
mutex_destroy(&zs->zs_excl);
mutex_destroy(&zs->zs_excl_hi);
if (za->za_flags_cv) {
kmem_free((caddr_t)za->za_flags_cv, sizeof (kcondvar_t));
za->za_flags_cv = NULL;
}
sprintf(name, "%d", ddi_get_instance(dip));
ddi_remove_minor_node(dip, name);
ddi_soft_state_free(mcpp_soft_p, ddi_get_instance(dip));
za->za_flags = 0;
zs->mcp_ops = NULL;
DEBUGF(1, (CE_CONT, "mcpp_detach%d: exiting ... \n",
ddi_get_instance(dip)));
return (DDI_SUCCESS);
}
static int
mcpp_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
{
int error = DDI_FAILURE;
int instance;
mcpp_soft_t *mcpp;
#ifdef lint
dip = dip;
#endif
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
instance = getminor((dev_t)arg);
mcpp = ddi_get_soft_state(mcpp_soft_p, instance);
if (mcpp) {
*result = mcpp->dip;
error = DDI_SUCCESS;
}
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)getminor((dev_t)arg);
error = DDI_SUCCESS;
break;
default:
break;
}
return (error);
}
/*
* Parallel Streams Routines.
*/
/*ARGSUSED2*/
static int
mcpp_open(queue_t *q, dev_t *dev, int flag, int sflag, cred_t *credp)
{
mcpcom_t *zs;
mcpaline_t *za;
mcpp_soft_t *mcpp;
mcp_state_t *softp;
int instance;
u_char status;
instance = getminor(*dev);
if ((mcpp = ddi_get_soft_state(mcpp_soft_p, instance)) == NULL)
return (ENODEV);
softp = (mcp_state_t *)mcpp->iface->priv;
za = &softp->mcpaline[N_MCP_ZSDEVS];
zs = &softp->mcpcom[N_MCP_ZSDEVS];
DEBUGF(1, (CE_CONT, "mcpp_open: unit = 0x%x\n", zs->zs_unit));
mutex_enter(&zs->zs_excl);
if (za->za_flags & ZAS_REFUSE) {
mutex_exit(&zs->zs_excl);
return (ENODEV);
}
if (za->za_common != (struct zscom *)zs) {
mutex_exit(&zs->zs_excl);
return (ENODEV);
}
mutex_enter(&zs->zs_excl_hi);
if (zs->mcp_ops == NULL)
zs->mcp_ops = &mcpp_ops;
if (zs->mcp_ops != &mcpp_ops) {
mutex_exit(&zs->zs_excl_hi);
mutex_exit(&zs->zs_excl);
return (ENODEV);
}
zs->zs_priv = (caddr_t) za;
za->za_dev = *dev;
za->za_flags |= ZAS_WOPEN;
if ((za->za_flags & ZAS_ISOPEN) == 0)
za->za_ttycommon.t_cflag = I_CFLAGS;
else if (za->za_ttycommon.t_flags & TS_XCLUDE) {
mutex_exit(&zs->zs_excl_hi);
mutex_exit(&zs->zs_excl);
return (ENXIO);
}
/* offline? */
mcpp_cntl(za, MCPIOGPR, (unsigned char *)&status);
DEBUGF(1, (CE_CONT, "mcpp_open: status = 0x%x\n",
(unsigned char)status));
if ((zs->zs_flags & MCPRIGNSLCT) == 0 &&
(status & (MCPRPE | MCPRSLCT)) != (MCPRPE | MCPRSLCT)) {
mutex_exit(&zs->zs_excl_hi);
mutex_exit(&zs->zs_excl);
if (status & MCPRINTSLCT)
cmn_err(CE_CONT, "Printer on mcpp%x is offline.\n",
zs->mc_unit);
return (ENXIO);
}
za->za_ttycommon.t_readq = q;
za->za_ttycommon.t_writeq = WR(q);
za->za_flags &= ~ZAS_WOPEN;
za->za_flags |= ZAS_ISOPEN | ZAS_CARR_ON;
mutex_exit(&zs->zs_excl_hi);
mutex_exit(&zs->zs_excl);
if (status & MCPRINTSLCT)
cmn_err(CE_CONT, "Printer on mcpp%x is online.\n",
zs->mc_unit);
q->q_ptr = WR(q)->q_ptr = (caddr_t) za;
qprocson(q);
return (0);
}
/*ARGSUSED1*/
static int
mcpp_close(queue_t *q, int flag, cred_t *credp)
{
mcpaline_t *za;
mcpcom_t *zs;
mcp_state_t *softp;
int temp_tid, temp_cid;
if ((za = (mcpaline_t *)q->q_ptr) == NULL)
return (ENODEV);
zs = (mcpcom_t *)za->za_common;
softp = (mcp_state_t *)zs->zs_state;
DEBUGF(1, (CE_CONT, "mcpp_close: unit = 0x%x\n", zs->zs_unit));
/* Did we already close this once? */
mutex_enter(&zs->zs_excl);
/*
* if we still have carrier, wait here until all data is gone
* or we are interrupted
*/
while ((za->za_flags & ZAS_CARR_ON) &&
((WR(q)->q_count != 0) || (za->za_flags & ZAS_BUSY)))
if (COND_WAIT_SIG(lbolt_condp, PRISAME,
(lock_t *)&zs->zs_excl, 0) == FALSE)
break;
if (za->za_flags & ZAS_BUSY) {
mutex_enter(&zs->zs_excl_hi);
(void) MCP_DMA_HALT(&softp->iface, zs->mcp_txdma);
mutex_exit(&zs->zs_excl_hi);
}
za->za_ocnt = 0;
/* Clear out device state. */
za->za_flags = 0;
ttycommon_close(&za->za_ttycommon);
COND_BROADCAST(za->za_flags_cv, NOPRMT);
temp_cid = za->za_wbufcid;
za->za_wbufcid = 0;
temp_tid = za->za_polltid;
za->za_polltid = 0;
q->q_ptr = WR(q)->q_ptr = NULL;
za->za_ttycommon.t_readq = NULL;
za->za_ttycommon.t_writeq = NULL;
mutex_exit(&zs->zs_excl);
/*
* Cancel outstanding "bufcall" request.
*/
if (temp_cid)
unbufcall(temp_cid);
/*
* Cancel outstanding timeout
*/
if (temp_tid)
untimeout(temp_tid);
qprocsoff(q);
return (0);
}
static int
mcpp_wput(queue_t *q, mblk_t *mp)
{
mcpaline_t *za;
mcpcom_t *zs;
mcp_state_t *softp;
ASSERT(q != NULL);
ASSERT(mp != NULL);
za = (mcpaline_t *)q->q_ptr;
zs = (mcpcom_t *)za->za_common;
softp = (mcp_state_t *)zs->zs_state;
DEBUGF(1, (CE_CONT, "mcpp_wput%d: db_type = 0x%x\n",
zs->mc_unit, mp->b_datap->db_type));
switch (mp->b_datap->db_type) {
case M_STOP:
mutex_enter(&zs->zs_excl);
za->za_flags |= ZAS_STOPPED;
mutex_exit(&zs->zs_excl);
freemsg(mp);
break;
case M_START:
mutex_enter(&zs->zs_excl);
if (za->za_flags & ZAS_STOPPED) {
za->za_flags &= ~ ZAS_STOPPED;
mcpp_start(za);
}
mutex_exit(&zs->zs_excl);
freemsg(mp);
break;
case M_IOCTL:
case M_IOCDATA:
mcpp_ioctl(za, q, mp);
break;
case M_FLUSH:
if (*mp->b_rptr & FLUSHW) {
/* Abort any output in progress. */
mutex_enter(&zs->zs_excl);
if (za->za_flags & ZAS_BUSY) {
mutex_enter(&zs->zs_excl_hi);
(void) MCP_DMA_HALT(&softp->iface,
zs->mcp_txdma);
mutex_exit(&zs->zs_excl_hi);
za->za_ocnt = 0;
za->za_flags &= ~ZAS_BUSY;
}
flushq(q, FLUSHDATA);
mutex_exit(&zs->zs_excl);
*mp->b_rptr &= ~FLUSHW;
}
if (*mp->b_rptr & FLUSHR) {
mutex_enter(&zs->zs_excl);
flushq(RD(q), FLUSHDATA);
mutex_exit(&zs->zs_excl);
qreply(q, mp);
} else
freemsg(mp);
/*
* We must make sure we process messages that survive the
* write-side flush. Without this call, the close protocol
* with ldterm can hang forever.
*/
mutex_enter(&zs->zs_excl);
mcpp_start(za);
mutex_exit(&zs->zs_excl);
break;
case M_BREAK:
case M_DELAY:
case M_DATA:
/*
* Queue the message up to be transmitted, and poke the start
* routine.
*/
mutex_enter(&zs->zs_excl);
(void) putq(q, mp);
mcpp_start(za);
mutex_exit(&zs->zs_excl);
break;
default:
freemsg(mp);
break;
}
return (0);
}
static int
mcpp_ioctl(mcpaline_t *za, queue_t *q, mblk_t *mp)
{
struct iocblk *iocp;
mcpcom_t *zs = (mcpcom_t *)za->za_common;
int error = 0;
u_char flags;
caddr_t uaddr;
iocp = (struct iocblk *)mp->b_rptr;
switch (mp->b_datap->db_type) {
case M_IOCDATA : {
struct copyresp *csp;
DEBUGF(1, (CE_CONT, "mcpp_ioctl%d: incoming iocdata\n",
zs->mc_unit));
csp = (struct copyresp *)(void *)mp->b_rptr;
if (csp->cp_rval != 0) {
DEBUGF(1, (CE_CONT, "mcpp_ioctl: iocdata rval = %d\n",
csp->cp_rval));
error = EIO;
break;
}
if (csp->cp_private != (mblk_t *)-1) {
mutex_enter(&zs->zs_excl);
mutex_enter(&zs->zs_excl_hi);
DEBUGF(1, (CE_CONT, "mcpp_ioctl: handling MCPIOSPR\n"));
mcpp_cntl(za, iocp->ioc_cmd,
(unsigned char *)mp->b_cont->b_rptr);
mutex_exit(&zs->zs_excl_hi);
mutex_exit(&zs->zs_excl);
}
iocp->ioc_count = 0;
iocp->ioc_error = 0;
mp->b_datap->db_type = M_IOCACK;
qreply(q, mp);
return (0);
}
case M_IOCTL : {
/*
* If 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.
*/
if (za->za_ttycommon.t_iocpending != NULL) {
freemsg(za->za_ttycommon.t_iocpending);
za->za_ttycommon.t_iocpending = NULL;
}
DEBUGF(1, (CE_CONT, "mcpp_ioctl%d: incoming ioctl\n",
zs->mc_unit));
switch (iocp->ioc_cmd) {
case MCPIOGPR: {
DEBUGF(1, (CE_CONT, "mcpp_ioctl: handling MCPIOGPR\n"));
uaddr = *(caddr_t *)(void *)mp->b_cont->b_rptr;
freemsg(mp->b_cont);
if ((mp->b_cont = allocb(1, BPRI_HI)) == NULL) {
/*
* This is so rare and so exceptional,
* don't bother with the callback.
*/
error = EINVAL;
break;
}
mutex_enter(&zs->zs_excl);
mutex_enter(&zs->zs_excl_hi);
mcpp_cntl(za, iocp->ioc_cmd, (u_char *)&flags);
mutex_exit(&zs->zs_excl_hi);
mutex_exit(&zs->zs_excl);
mp->b_cont->b_rptr[0] = flags;
mp->b_cont->b_wptr++;
DEBUGF(1, (CE_CONT, "mcpp_ioctl: flags = 0x%2x\n",
flags));
mcpp_copyout(q, mp, uaddr, sizeof (u_char));
return (0);
}
case MCPIOSPR: {
uaddr = *(caddr_t *)(void *)mp->b_cont->b_rptr;
mcpp_copyin(q, mp, uaddr, sizeof (u_char));
return (0);
}
default:
DEBUGF(1, (CE_CONT, "mcpp_ioctl: wierd request= %d\n",
iocp->ioc_cmd));
/* We don't understand it either. */
error = EINVAL;
break;
}
}
}
if (error) {
DEBUGF(1, (CE_CONT, "mcpp_ioctl: error = %d \n", error));
iocp->ioc_error = error;
mp->b_datap->db_type = M_IOCNAK;
}
DEBUGF(1, (CE_CONT, "mcpp_ioctl: error = %d \n", error));
qreply(q, mp);
return (0);
}
static void
mcpp_copyin(queue_t *q, mblk_t *mp, caddr_t addr, uint_t len)
{
struct copyreq *cqp;
cqp = (struct copyreq *)(void *)mp->b_rptr;
mp->b_wptr = mp->b_wptr + sizeof (struct copyreq);
cqp->cq_addr = addr;
cqp->cq_size = len;
cqp->cq_private = (mblk_t *)(void *)addr;
cqp->cq_flag = 0;
if (mp->b_cont != NULL) {
freemsg(mp->b_cont);
mp->b_cont = NULL;
}
mp->b_datap->db_type = M_COPYIN;
qreply(q, mp);
}
static void
mcpp_copyout(queue_t *q, mblk_t *mp, caddr_t addr, uint_t len)
{
struct copyreq *cqp;
cqp = (struct copyreq *)(void *)mp->b_rptr;
mp->b_wptr = mp->b_rptr + sizeof (struct copyreq);
cqp->cq_addr = addr;
cqp->cq_size = len;
cqp->cq_private = (mblk_t *)-1;
cqp->cq_flag = 0;
mp->b_datap->db_type = M_COPYOUT;
qreply(q, mp);
}
static void
mcpp_start(mcpaline_t *za)
{
mcpcom_t *zs;
mcp_state_t *softp;
int cc, avail_bytes;
u_char *cp;
queue_t *q;
mblk_t *bp, *nbp;
int max_buf_size;
ASSERT(za != NULL);
zs = (mcpcom_t *)za->za_common;
ASSERT(mutex_owned(&zs->zs_excl));
softp = (mcp_state_t *)zs->zs_state;
/*
* If we're waiting for a break timeout to expire, don't grab
* anything new.
*/
if (za->za_flags & ZAS_BREAK)
return;
/*
* If we're waiting for a delay timeout to expire or for the current
* transmission to complete, don't grab anything new.
*/
if (za->za_flags & (ZAS_BUSY | ZAS_DELAY))
return;
/*
* Hopefully we're attached to a stream.
*/
if ((q = za->za_ttycommon.t_writeq) == NULL)
return;
/*
* Set up to copy up to a bufferful of bytes into the MCP buffer.
*/
max_buf_size = PR_BSZ;
cp = za->za_dmabuf;
avail_bytes = max_buf_size;
while ((bp = getq(q)) != NULL) {
/*
* 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.
*/
switch (bp->b_datap->db_type) {
case M_BREAK:
if (avail_bytes != max_buf_size) {
(void) putbq(q, bp);
goto transmit;
}
(void) timeout(mcpp_restart, (caddr_t)za, hz / 4);
za->za_flags |= ZAS_BREAK;
freemsg(bp);
return;
case M_DELAY:
if (avail_bytes != max_buf_size) {
(void) putbq(q, bp);
goto transmit;
}
/*
* Arrange for "mcp_restart" to be called when the delay
* expires; it will turn ZAS_DELAY off, then call
* "mcpstart" to grab the next message.
*/
(void) timeout(mcpp_restart, (caddr_t)za,
(int)(*(unsigned char *)bp->b_rptr + 6));
za->za_flags |= ZAS_DELAY;
freemsg(bp);
return;
case M_IOCTL:
if (avail_bytes != max_buf_size) {
(void) putbq(q, bp);
goto transmit;
}
/*
* 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.
*/
mutex_exit(&zs->zs_excl);
mcpp_ioctl(za, q, bp);
mutex_enter(&zs->zs_excl);
continue;
}
/*
* We have data to transmit. If output is stopped, put it
* back and try again later.
*/
if (za->za_flags & ZAS_STOPPED) {
(void) putbq(q, bp);
return;
}
za->za_ocnt = 0;
while (bp != NULL) {
while ((cc = bp->b_wptr - bp->b_rptr) != 0) {
if (avail_bytes == 0) {
(void) putbq(q, bp);
goto transmit;
}
cc = MIN(cc, avail_bytes);
/* Copy the bytes to the card. */
bcopy((caddr_t)bp->b_rptr, (caddr_t)cp, cc);
/* Update pointers and counters. */
cp += cc;
avail_bytes -= cc;
bp->b_rptr += cc;
za->za_ocnt += cc;
}
if (bp->b_wptr == bp->b_rptr) {
if (bp->b_cont != NULL) {
nbp = bp;
bp = bp->b_cont;
nbp->b_cont = NULL;
freemsg(nbp);
if (avail_bytes == 0) {
(void) putbq(q, bp);
goto transmit;
}
} else {
freemsg(bp);
goto transmit;
}
}
}
}
transmit:
if (za->za_ocnt) {
mutex_enter(&zs->zs_excl_hi);
MCP_DMA_START(&softp->iface, zs->mcp_txdma,
(char *)za->za_dmabuf, za->za_ocnt);
za->za_flags |= ZAS_BUSY;
zs->zs_flags |= MCP_WAIT_DMA;
mutex_exit(&zs->zs_excl_hi);
}
}
static void
mcpp_restart(mcpaline_t *za)
{
mcpcom_t *zs = (mcpcom_t *)za->za_common;
queue_t *q;
DEBUGF(1, (CE_CONT, "mcpp_start%d: restarting mcpp_start\n",
zs->mc_unit));
mutex_enter(&zs->zs_excl);
/*
* If break timer expired, turn off break bit.
*/
za->za_flags &= ~(ZAS_DELAY | ZAS_BREAK);
if ((q = za->za_ttycommon.t_writeq) != NULL)
enterq(q);
mcpp_start(za);
if (q != NULL)
leaveq(q);
mutex_exit(&zs->zs_excl);
}
/*
* Parallel Port Interrrupt Routines.
*/
static int
mcpp_txend(mcpcom_t *zs)
{
mcpaline_t *za = (mcpaline_t *)zs->zs_priv;
DEBUGF(1, (CE_CONT, "mcpp_txend%d: xmit end intr\n", zs->zs_unit));
mutex_enter(&zs->zs_excl);
if (za->za_flags & ZAS_BUSY) {
/*
* If a transmission has finished, indicate that it is
* finished, and start that line up again.
*/
za->za_flags &= ~ZAS_BUSY;
za->za_ocnt = 0;
mcpp_start(za);
}
mutex_exit(&zs->zs_excl);
return (0);
}
static int
mcpp_pe(mcpcom_t *zs)
{
struct _ciochip_ *ciop = &zs->mcp_addr->cio;
u_char uc;
if (zs->zs_flags & MCPRINTPE && !(ciop->portb_data & MCPRPE)) {
cmn_err(CE_WARN, "Printer on mcpp%x is out of paper.\n",
zs->mc_unit);
CIO_READ(ciop, CIO_PB_PP, uc);
uc |= MCPRPE;
CIO_WRITE(ciop, CIO_PB_PP, uc);
} else {
cmn_err(CE_WARN, "Printer on mcpp%x: paper ok.\n",
zs->mc_unit);
CIO_READ(ciop, CIO_PB_PP, uc);
uc &= ~MCPRPE;
CIO_WRITE(ciop, CIO_PB_PP, uc);
}
return (0);
}
static int
mcpp_slct(mcpcom_t *zs)
{
struct _ciochip_ *ciop = &zs->mcp_addr->cio;
u_char uc;
if ((zs->zs_flags & MCPRINTSLCT) && !(ciop->portb_data & MCPRSLCT)) {
cmn_err(CE_CONT, "Printer on mcpp%x is offline.\n",
zs->mc_unit);
CIO_READ(ciop, CIO_PB_PP, uc);
uc |= MCPRSLCT;
CIO_WRITE(ciop, CIO_PB_PP, uc);
} else {
cmn_err(CE_CONT, "Printer on mcpp%x is online.\n",
zs->mc_unit);
CIO_READ(ciop, CIO_PB_PP, uc);
uc &= ~MCPRSLCT;
CIO_WRITE(ciop, CIO_PB_PP, uc);
}
return (0);
}
/*
* Parallel Port Maniputlation routines.
*/
static void
mcpp_cntl(mcpaline_t *za, int cmd, unsigned char *datap)
{
mcpcom_t *zs = (struct mcpcom *) za->za_common;
struct _ciochip_ *cp = &zs->mcp_addr->cio;
ASSERT(mutex_owned(&zs->zs_excl));
ASSERT(mutex_owned(&zs->zs_excl_hi));
/*
* the sense of the MCPRDIAG flag is reversed on the CIO,
* it is cleared when the printer is in diag mode.
*/
switch (cmd) {
case MCPIOSPR:
DEBUGF(1, (CE_CONT,
"mcpp_cntl: datap, zs->zs_flags = 0x%2x, 0x%2x\n",
*datap, zs->zs_flags));
if ((*datap ^ zs->zs_flags) & (MCPRINTSLCT | MCPRINTPE)) {
zs->zs_flags =
(zs->zs_flags & ~(MCPRINTSLCT | MCPRINTPE)) |
(*datap & (MCPRINTSLCT | MCPRINTPE));
}
if ((*datap ^ zs->zs_flags) & MCPRDIAG) {
zs->zs_flags = (zs->zs_flags & ~MCPRDIAG) |
(*datap & MCPRDIAG);
cp->portc_data = (cp->portc_data & ~MCPRDIAG & 0xf) |
(*datap & MCPRDIAG);
}
/*FALLTHROUGH*/
case MCPIOGPR:
*datap = zs->zs_flags & (MCPRINTSLCT | MCPRINTPE | MCPRIGNSLCT);
*datap |= cp->portb_data & (MCPRPE | MCPRSLCT);
*datap |= cp->portc_data & (MCPRVMEINT | MCPRDIAG);
DEBUGF(1, (CE_CONT, "mcpp_cntl: datap = 0x%2x\n", *datap));
break;
}
}