/*ARGSUSED*/ static int lx_ptm_close(dev_t dev, int flag, int otyp, cred_t *credp) { ldi_handle_t lh; major_t maj; minor_t min, lastmin; uint_t index; int err; index = DEVT_TO_INDEX(dev); /* * we must cleanup all the state associated with this major/minor * terminal pair before actually closing the ptm master device. * this is required because once the close of the ptm device is * complete major/minor terminal pair is immediatly available for * re-use in any zone. */ /* free up our saved reference for this layered handle */ lh = lx_ptm_lh_remove(index); /* unconfigure autopush for the associated terminal slave device */ maj = lps.lps_pts_major; min = index; lastmin = 0; do { /* * we loop here because we don't want to release this ptm * node if autopush can't be disabled on the associated * slave device because then bad things could happen if * another brand were to get this terminal allocated * to them. * * XXX should we ever give up? */ err = kstr_autopush(CLR_AUTOPUSH, &maj, &min, &lastmin, 0, NULL); } while (err != 0); err = ldi_close(lh, flag, credp); /* * note that we don't have to bother with changing the permissions * on the associated slave device here. the reason is that no one * can actually open the device untill it's associated master * device is re-opened, which will result in the permissions on * it being reset. */ return (err); }
/*ARGSUSED1*/ static int zc_close(queue_t *rqp, int flag, cred_t *credp) { queue_t *wqp; mblk_t *bp; zc_state_t *zcs; major_t major; minor_t minor; zcs = (zc_state_t *)rqp->q_ptr; if (rqp == zcs->zc_master_rdq) { DBG("Closing master side"); zcs->zc_master_rdq = NULL; zcs->zc_state &= ~ZC_STATE_MOPEN; /* * qenable slave side write queue so that it can flush * its messages as master's read queue is going away */ if (zcs->zc_slave_rdq != NULL) { qenable(WR(zcs->zc_slave_rdq)); } qprocsoff(rqp); WR(rqp)->q_ptr = rqp->q_ptr = NULL; } else if (rqp == zcs->zc_slave_rdq) { DBG("Closing slave side"); zcs->zc_state &= ~ZC_STATE_SOPEN; zcs->zc_slave_rdq = NULL; wqp = WR(rqp); while ((bp = getq(wqp)) != NULL) { if (zcs->zc_master_rdq != NULL) putnext(zcs->zc_master_rdq, bp); else if (bp->b_datap->db_type == M_IOCTL) miocnak(wqp, bp, 0, 0); else freemsg(bp); } /* * Qenable master side write queue so that it can flush its * messages as slaves's read queue is going away. */ if (zcs->zc_master_rdq != NULL) qenable(WR(zcs->zc_master_rdq)); qprocsoff(rqp); WR(rqp)->q_ptr = rqp->q_ptr = NULL; /* * Clear the sad configuration so that reopening doesn't fail * to set up sad configuration. */ major = ddi_driver_major(zcs->zc_devinfo); minor = ddi_get_instance(zcs->zc_devinfo) << 1 | ZC_SLAVE_MINOR; (void) kstr_autopush(CLR_AUTOPUSH, &major, &minor, NULL, NULL, NULL); } return (0); }
/*ARGSUSED*/ static int zc_slave_open(zc_state_t *zcs, queue_t *rqp, /* pointer to the read side queue */ dev_t *devp, /* pointer to stream tail's dev */ int oflag, /* the user open(2) supplied flags */ int sflag, /* open state flag */ cred_t *credp) /* credentials */ { mblk_t *mop; struct stroptions *sop; major_t major; minor_t minor; minor_t lastminor; uint_t anchorindex; /* * The slave side can be opened as many times as needed. */ if ((zcs->zc_state & ZC_STATE_SOPEN) != 0) { ASSERT((rqp != NULL) && (WR(rqp)->q_ptr == zcs)); return (0); } /* * Set up sad(7D) so that the necessary STREAMS modules will be in * place. A wrinkle is that 'ptem' must be anchored * in place (see streamio(7i)) because we always want the console to * have terminal semantics. */ minor = ddi_get_instance(zcs->zc_devinfo) << 1 | ZC_SLAVE_MINOR; major = ddi_driver_major(zcs->zc_devinfo); lastminor = 0; anchorindex = 1; if (kstr_autopush(SET_AUTOPUSH, &major, &minor, &lastminor, &anchorindex, zcons_mods) != 0) { DBG("zc_slave_open(): kstr_autopush() failed\n"); return (EIO); } if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) { DBG("zc_slave_open(): mop allocation failed\n"); return (ENOMEM); } zcs->zc_state |= ZC_STATE_SOPEN; /* * q_ptr stores driver private data; stash the soft state data on both * read and write sides of the queue. */ WR(rqp)->q_ptr = rqp->q_ptr = zcs; qprocson(rqp); /* * Must follow qprocson(), since we aren't ready to process until then. */ zcs->zc_slave_rdq = rqp; /* * set up hi/lo water marks on stream head read queue and add * controlling tty as needed. */ mop->b_datap->db_type = M_SETOPTS; mop->b_wptr += sizeof (struct stroptions); sop = (struct stroptions *)(void *)mop->b_rptr; sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY; sop->so_hiwat = _TTY_BUFSIZ; sop->so_lowat = 256; putnext(rqp, mop); return (0); }
/*ARGSUSED*/ static int lx_ptm_open(dev_t *devp, int flag, int otyp, cred_t *credp) { struct strioctl iocb; ptmptsopencb_t ppocb = { NULL, NULL }; ldi_handle_t lh; major_t maj, our_major = getmajor(*devp); minor_t min, lastmin; uint_t index, anchor = 1; dev_t ptm_dev; int err, rval = 0; /* * Don't support the FNDELAY flag and FNONBLOCK until we either * find a Linux app that opens /dev/ptmx with the O_NDELAY * or O_NONBLOCK flags explicitly, or until we create test cases * to determine how reads of master terminal devices opened with * these flags behave in different situations on Linux. Supporting * these flags will involve enhancing our read implementation * and changing the way it deals with EOF notifications. */ if (flag & (FNDELAY | FNONBLOCK)) return (ENOTSUP); /* * we're layered on top of the ptm driver so open that driver * first. (note that we're opening /dev/ptmx in the global * zone, not ourselves in the Linux zone.) */ err = ldi_open_by_name(LP_PTM_PATH, flag, credp, &lh, lps.lps_li); if (err != 0) return (err); /* get the devt returned by the ptmx open */ err = ldi_get_dev(lh, &ptm_dev); if (err != 0) { (void) ldi_close(lh, flag, credp); return (err); } /* * we're a cloning driver so here's well change the devt that we * return. the ptmx is also a cloning driver so we'll just use * it's minor number as our minor number (it already manages it's * minor name space so no reason to duplicate the effort.) */ index = getminor(ptm_dev); *devp = makedevice(our_major, INDEX_TO_MINOR(index)); /* Get a callback function to query if the pts device is open. */ iocb.ic_cmd = PTMPTSOPENCB; iocb.ic_timout = 0; iocb.ic_len = sizeof (ppocb); iocb.ic_dp = (char *)&ppocb; err = ldi_ioctl(lh, I_STR, (intptr_t)&iocb, FKIOCTL, kcred, &rval); if ((err != 0) || (rval != 0)) { (void) ldi_close(lh, flag, credp); return (EIO); /* XXX return something else here? */ } ASSERT(ppocb.ppocb_func != NULL); /* * now setup autopush for the terminal slave device. this is * necessary so that when a Linux program opens the device we * can push required strmod modules onto the stream. in Solaris * this is normally done by the application that actually * allocates the terminal. */ maj = lps.lps_pts_major; min = index; lastmin = 0; err = kstr_autopush(SET_AUTOPUSH, &maj, &min, &lastmin, &anchor, lx_pts_mods); if (err != 0) { (void) ldi_close(lh, flag, credp); return (EIO); /* XXX return something else here? */ } /* save off this layered handle for future accesses */ lx_ptm_lh_insert(index, lh); lx_ptm_lh_set_ppocb(index, &ppocb); return (0); }