/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * 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; } }