11ae08745Sheppo /*
21ae08745Sheppo * CDDL HEADER START
31ae08745Sheppo *
41ae08745Sheppo * The contents of this file are subject to the terms of the
51ae08745Sheppo * Common Development and Distribution License (the "License").
61ae08745Sheppo * You may not use this file except in compliance with the License.
71ae08745Sheppo *
81ae08745Sheppo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91ae08745Sheppo * or http://www.opensolaris.org/os/licensing.
101ae08745Sheppo * See the License for the specific language governing permissions
111ae08745Sheppo * and limitations under the License.
121ae08745Sheppo *
131ae08745Sheppo * When distributing Covered Code, include this CDDL HEADER in each
141ae08745Sheppo * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151ae08745Sheppo * If applicable, add the following below this CDDL HEADER, with the
161ae08745Sheppo * fields enclosed by brackets "[]" replaced with your own identifying
171ae08745Sheppo * information: Portions Copyright [yyyy] [name of copyright owner]
181ae08745Sheppo *
191ae08745Sheppo * CDDL HEADER END
201ae08745Sheppo */
211ae08745Sheppo
221ae08745Sheppo /*
23*d3d50737SRafael Vanoni * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
241ae08745Sheppo * Use is subject to license terms.
251ae08745Sheppo */
261ae08745Sheppo
271ae08745Sheppo
281ae08745Sheppo #include <sys/types.h>
291ae08745Sheppo #include <sys/file.h>
301ae08745Sheppo #include <sys/errno.h>
311ae08745Sheppo #include <sys/uio.h>
321ae08745Sheppo #include <sys/open.h>
331ae08745Sheppo #include <sys/cred.h>
341ae08745Sheppo #include <sys/kmem.h>
351ae08745Sheppo #include <sys/conf.h>
361ae08745Sheppo #include <sys/cmn_err.h>
371ae08745Sheppo #include <sys/ksynch.h>
381ae08745Sheppo #include <sys/modctl.h>
391ae08745Sheppo #include <sys/stat.h> /* needed for S_IFBLK and S_IFCHR */
401ae08745Sheppo #include <sys/debug.h>
411ae08745Sheppo #include <sys/sysmacros.h>
421ae08745Sheppo #include <sys/types.h>
431ae08745Sheppo #include <sys/cred.h>
441ae08745Sheppo #include <sys/promif.h>
451ae08745Sheppo #include <sys/ddi.h>
461ae08745Sheppo #include <sys/sunddi.h>
471ae08745Sheppo #include <sys/cyclic.h>
481ae08745Sheppo #include <sys/note.h>
491ae08745Sheppo #include <sys/mach_descrip.h>
501ae08745Sheppo #include <sys/mdeg.h>
511ae08745Sheppo #include <sys/ldc.h>
521ae08745Sheppo #include <sys/vldc_impl.h>
531ae08745Sheppo
541ae08745Sheppo /*
551ae08745Sheppo * Function prototypes.
561ae08745Sheppo */
571ae08745Sheppo
581ae08745Sheppo /* DDI entrypoints */
591ae08745Sheppo static int vldc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
601ae08745Sheppo static int vldc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
611ae08745Sheppo static int vldc_open(dev_t *devp, int flag, int otyp, cred_t *cred);
621ae08745Sheppo static int vldc_close(dev_t dev, int flag, int otyp, cred_t *cred);
631ae08745Sheppo static int vldc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
641ae08745Sheppo cred_t *credp, int *rvalp);
651ae08745Sheppo static int vldc_read(dev_t dev, struct uio *uiop, cred_t *credp);
661ae08745Sheppo static int vldc_write(dev_t dev, struct uio *uiop, cred_t *credp);
671ae08745Sheppo static int vldc_chpoll(dev_t dev, short events, int anyyet,
681ae08745Sheppo short *reventsp, struct pollhead **phpp);
691ae08745Sheppo
701ae08745Sheppo /* Internal functions */
711ae08745Sheppo static uint_t i_vldc_cb(uint64_t event, caddr_t arg);
721ae08745Sheppo static int i_vldc_mdeg_cb(void *cb_argp, mdeg_result_t *resp);
731ae08745Sheppo static int i_vldc_mdeg_register(vldc_t *vldcp);
741ae08745Sheppo static int i_vldc_mdeg_unregister(vldc_t *vldcp);
751ae08745Sheppo static int i_vldc_add_port(vldc_t *vldcp, md_t *mdp, mde_cookie_t node);
761ae08745Sheppo static int i_vldc_remove_port(vldc_t *vldcp, uint_t portno);
771ae08745Sheppo static int i_vldc_close_port(vldc_t *vldcp, uint_t portno);
781ae08745Sheppo
791ae08745Sheppo /* soft state structure */
801ae08745Sheppo static void *vldc_ssp;
811ae08745Sheppo
821ae08745Sheppo /*
831ae08745Sheppo * Matching criteria passed to the MDEG to register interest
841ae08745Sheppo * in changes to 'virtual-device-port' nodes identified by their
851ae08745Sheppo * 'id' property.
861ae08745Sheppo */
871ae08745Sheppo static md_prop_match_t vport_prop_match[] = {
881ae08745Sheppo { MDET_PROP_VAL, "id" },
891ae08745Sheppo { MDET_LIST_END, NULL }
901ae08745Sheppo };
911ae08745Sheppo
921ae08745Sheppo static mdeg_node_match_t vport_match = { "virtual-device-port",
931ae08745Sheppo vport_prop_match };
941ae08745Sheppo
951ae08745Sheppo /*
961ae08745Sheppo * Specification of an MD node passed to the MDEG to filter any
971ae08745Sheppo * 'virtual-device-port' nodes that do not belong to the specified
981ae08745Sheppo * node. This template is copied for each vldc instance and filled
991ae08745Sheppo * in with the appropriate 'name' and 'cfg-handle' values before
1001ae08745Sheppo * being passed to the MDEG.
1011ae08745Sheppo */
1021ae08745Sheppo static mdeg_prop_spec_t vldc_prop_template[] = {
1031ae08745Sheppo { MDET_PROP_STR, "name", NULL },
1041ae08745Sheppo { MDET_PROP_VAL, "cfg-handle", NULL },
1051ae08745Sheppo { MDET_LIST_END, NULL, NULL }
1061ae08745Sheppo };
1071ae08745Sheppo
1081ae08745Sheppo #define VLDC_MDEG_PROP_NAME(specp) ((specp)[0].ps_str)
1091ae08745Sheppo #define VLDC_SET_MDEG_PROP_NAME(specp, name) ((specp)[0].ps_str = (name))
1101ae08745Sheppo #define VLDC_SET_MDEG_PROP_INST(specp, inst) ((specp)[1].ps_val = (inst))
1111ae08745Sheppo
1121ae08745Sheppo
1131ae08745Sheppo static struct cb_ops vldc_cb_ops = {
1141ae08745Sheppo vldc_open, /* open */
1151ae08745Sheppo vldc_close, /* close */
1161ae08745Sheppo nodev, /* strategy */
1171ae08745Sheppo nodev, /* print */
1181ae08745Sheppo nodev, /* dump */
1191ae08745Sheppo vldc_read, /* read */
1201ae08745Sheppo vldc_write, /* write */
1211ae08745Sheppo vldc_ioctl, /* ioctl */
1221ae08745Sheppo nodev, /* devmap */
1231ae08745Sheppo nodev, /* mmap */
1241ae08745Sheppo ddi_segmap, /* segmap */
1251ae08745Sheppo vldc_chpoll, /* chpoll */
1261ae08745Sheppo ddi_prop_op, /* prop_op */
1271ae08745Sheppo NULL, /* stream */
1281ae08745Sheppo D_NEW | D_MP /* flag */
1291ae08745Sheppo };
1301ae08745Sheppo
1311ae08745Sheppo static struct dev_ops vldc_ops = {
1321ae08745Sheppo DEVO_REV, /* rev */
1331ae08745Sheppo 0, /* ref count */
1341ae08745Sheppo ddi_getinfo_1to1, /* getinfo */
1351ae08745Sheppo nulldev, /* identify */
1361ae08745Sheppo nulldev, /* probe */
1371ae08745Sheppo vldc_attach, /* attach */
1381ae08745Sheppo vldc_detach, /* detach */
1391ae08745Sheppo nodev, /* reset */
1401ae08745Sheppo &vldc_cb_ops, /* cb_ops */
14119397407SSherry Moore (struct bus_ops *)NULL, /* bus_ops */
14219397407SSherry Moore NULL, /* power */
14319397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */
1441ae08745Sheppo };
1451ae08745Sheppo
1461ae08745Sheppo extern struct mod_ops mod_driverops;
1471ae08745Sheppo
1481ae08745Sheppo static struct modldrv md = {
1491ae08745Sheppo &mod_driverops, /* Type - it is a driver */
15019397407SSherry Moore "sun4v Virtual LDC Driver", /* Name of the module */
1511ae08745Sheppo &vldc_ops, /* driver specific ops */
1521ae08745Sheppo };
1531ae08745Sheppo
1541ae08745Sheppo static struct modlinkage ml = {
1551ae08745Sheppo MODREV_1,
1561ae08745Sheppo &md,
1571ae08745Sheppo NULL
1581ae08745Sheppo };
1591ae08745Sheppo
1601ae08745Sheppo /* maximum MTU and cookie size tunables */
1611ae08745Sheppo uint32_t vldc_max_mtu = VLDC_MAX_MTU;
1621ae08745Sheppo uint64_t vldc_max_cookie = VLDC_MAX_COOKIE;
1631ae08745Sheppo
164cb112a14Slm66018 /*
1654d39be2bSsg70180 * when ldc_close() returns EAGAIN, it is retried with a wait
1664d39be2bSsg70180 * of 'vldc_close_delay' between each retry.
167cb112a14Slm66018 */
1684d39be2bSsg70180 static clock_t vldc_close_delay = VLDC_CLOSE_DELAY;
1691ae08745Sheppo
1701ae08745Sheppo #ifdef DEBUG
1711ae08745Sheppo
1721ae08745Sheppo /*
1731ae08745Sheppo * Print debug messages
1741ae08745Sheppo *
1751ae08745Sheppo * set vldcdbg to 0x7 to enable all messages
1761ae08745Sheppo *
1771ae08745Sheppo * 0x4 - Warnings
1781ae08745Sheppo * 0x2 - All debug messages (most verbose)
1791ae08745Sheppo * 0x1 - Minimal debug messages
1801ae08745Sheppo */
1811ae08745Sheppo
1821ae08745Sheppo int vldcdbg = 0x0;
1831ae08745Sheppo
1841ae08745Sheppo static void
vldcdebug(const char * fmt,...)1851ae08745Sheppo vldcdebug(const char *fmt, ...)
1861ae08745Sheppo {
1871ae08745Sheppo char buf[512];
1881ae08745Sheppo va_list ap;
1891ae08745Sheppo
1901ae08745Sheppo va_start(ap, fmt);
1911ae08745Sheppo (void) vsnprintf(buf, sizeof (buf), fmt, ap);
1921ae08745Sheppo va_end(ap);
1931ae08745Sheppo
1941ae08745Sheppo cmn_err(CE_CONT, "?%s", buf);
1951ae08745Sheppo }
1961ae08745Sheppo
1971ae08745Sheppo #define D1 if (vldcdbg & 0x01) vldcdebug
1981ae08745Sheppo #define D2 if (vldcdbg & 0x02) vldcdebug
1991ae08745Sheppo #define DWARN if (vldcdbg & 0x04) vldcdebug
2001ae08745Sheppo
2011ae08745Sheppo #else /* not DEBUG */
2021ae08745Sheppo
2031ae08745Sheppo #define D1 if (0) printf
2041ae08745Sheppo #define D2 if (0) printf
2051ae08745Sheppo #define DWARN if (0) printf
2061ae08745Sheppo
2071ae08745Sheppo #endif /* not DEBUG */
2081ae08745Sheppo
2091ae08745Sheppo
2101ae08745Sheppo /* _init(9E): initialize the loadable module */
2111ae08745Sheppo int
_init(void)2121ae08745Sheppo _init(void)
2131ae08745Sheppo {
2141ae08745Sheppo int error;
2151ae08745Sheppo
2161ae08745Sheppo /* init the soft state structure */
2171ae08745Sheppo error = ddi_soft_state_init(&vldc_ssp, sizeof (vldc_t), 1);
2181ae08745Sheppo if (error != 0) {
2191ae08745Sheppo return (error);
2201ae08745Sheppo }
2211ae08745Sheppo
2221ae08745Sheppo /* Link the driver into the system */
2231ae08745Sheppo error = mod_install(&ml);
2241ae08745Sheppo
2251ae08745Sheppo return (error);
2261ae08745Sheppo }
2271ae08745Sheppo
2281ae08745Sheppo /* _info(9E): return information about the loadable module */
2291ae08745Sheppo int
_info(struct modinfo * modinfop)2301ae08745Sheppo _info(struct modinfo *modinfop)
2311ae08745Sheppo {
2321ae08745Sheppo /* Report status of the dynamically loadable driver module */
2331ae08745Sheppo return (mod_info(&ml, modinfop));
2341ae08745Sheppo }
2351ae08745Sheppo
2361ae08745Sheppo /* _fini(9E): prepare the module for unloading. */
2371ae08745Sheppo int
_fini(void)2381ae08745Sheppo _fini(void)
2391ae08745Sheppo {
2401ae08745Sheppo int error;
2411ae08745Sheppo
2421ae08745Sheppo /* Unlink the driver module from the system */
2431ae08745Sheppo if ((error = mod_remove(&ml)) == 0) {
2441ae08745Sheppo /*
2451ae08745Sheppo * We have successfully "removed" the driver.
2461ae08745Sheppo * destroy soft state
2471ae08745Sheppo */
2481ae08745Sheppo ddi_soft_state_fini(&vldc_ssp);
2491ae08745Sheppo }
2501ae08745Sheppo
2511ae08745Sheppo return (error);
2521ae08745Sheppo }
2531ae08745Sheppo
2541ae08745Sheppo /* ldc callback */
2551ae08745Sheppo static uint_t
i_vldc_cb(uint64_t event,caddr_t arg)2561ae08745Sheppo i_vldc_cb(uint64_t event, caddr_t arg)
2571ae08745Sheppo {
258a8ea4edeSnarayan int rv;
2591ae08745Sheppo vldc_port_t *vport = (vldc_port_t *)arg;
260a8ea4edeSnarayan ldc_status_t old_status;
2611ae08745Sheppo short pollevents = 0;
2621ae08745Sheppo
263cb112a14Slm66018 ASSERT(vport != NULL);
264cb112a14Slm66018 ASSERT(vport->minorp != NULL);
265cb112a14Slm66018
2663af08d82Slm66018 D1("i_vldc_cb: vldc@%d:%d callback invoked, channel=0x%lx, "
2673af08d82Slm66018 "event=0x%lx\n", vport->inst, vport->number, vport->ldc_id, event);
2681ae08745Sheppo
269cb112a14Slm66018 /* ensure the port can't be destroyed while we are handling the cb */
270cb112a14Slm66018 mutex_enter(&vport->minorp->lock);
271cb112a14Slm66018
2724d39be2bSsg70180 if (vport->status == VLDC_PORT_CLOSED) {
2734d39be2bSsg70180 return (LDC_SUCCESS);
2744d39be2bSsg70180 }
2754d39be2bSsg70180
276a8ea4edeSnarayan old_status = vport->ldc_status;
277a8ea4edeSnarayan rv = ldc_status(vport->ldc_handle, &vport->ldc_status);
278a8ea4edeSnarayan if (rv != 0) {
279a8ea4edeSnarayan DWARN("i_vldc_cb: vldc@%d:%d could not get ldc status, "
280a8ea4edeSnarayan "rv=%d\n", vport->inst, vport->number, rv);
281cb112a14Slm66018 mutex_exit(&vport->minorp->lock);
282a8ea4edeSnarayan return (LDC_SUCCESS);
283a8ea4edeSnarayan }
284a8ea4edeSnarayan
2851ae08745Sheppo if (event & LDC_EVT_UP) {
2861ae08745Sheppo pollevents |= POLLOUT;
2871ae08745Sheppo vport->hanged_up = B_FALSE;
2881ae08745Sheppo
2891ae08745Sheppo } else if (event & LDC_EVT_RESET) {
2903af08d82Slm66018 /*
291a8ea4edeSnarayan * Mark the port in reset, if it is not CLOSED and
292a8ea4edeSnarayan * the channel was previously in LDC_UP state. This
293a8ea4edeSnarayan * implies that the port cannot be used until it has
294a8ea4edeSnarayan * been closed and reopened.
2953af08d82Slm66018 */
2964d39be2bSsg70180 if (old_status == LDC_UP) {
2973af08d82Slm66018 vport->status = VLDC_PORT_RESET;
298a8ea4edeSnarayan vport->hanged_up = B_TRUE;
299a8ea4edeSnarayan pollevents = POLLHUP;
300a8ea4edeSnarayan } else {
301a8ea4edeSnarayan rv = ldc_up(vport->ldc_handle);
302a8ea4edeSnarayan if (rv) {
303a8ea4edeSnarayan DWARN("i_vldc_cb: vldc@%d:%d cannot bring "
304a8ea4edeSnarayan "channel UP rv=%d\n", vport->inst,
305a8ea4edeSnarayan vport->number, rv);
306cb112a14Slm66018 mutex_exit(&vport->minorp->lock);
3073af08d82Slm66018 return (LDC_SUCCESS);
308a8ea4edeSnarayan }
309a8ea4edeSnarayan rv = ldc_status(vport->ldc_handle, &vport->ldc_status);
310a8ea4edeSnarayan if (rv != 0) {
311a8ea4edeSnarayan DWARN("i_vldc_cb: vldc@%d:%d could not get "
312a8ea4edeSnarayan "ldc status, rv=%d\n", vport->inst,
313a8ea4edeSnarayan vport->number, rv);
314cb112a14Slm66018 mutex_exit(&vport->minorp->lock);
315a8ea4edeSnarayan return (LDC_SUCCESS);
316a8ea4edeSnarayan }
317a8ea4edeSnarayan if (vport->ldc_status == LDC_UP) {
318a8ea4edeSnarayan pollevents |= POLLOUT;
319a8ea4edeSnarayan vport->hanged_up = B_FALSE;
320a8ea4edeSnarayan }
321a8ea4edeSnarayan }
3223af08d82Slm66018
3233af08d82Slm66018 } else if (event & LDC_EVT_DOWN) {
3243af08d82Slm66018 /*
325a8ea4edeSnarayan * The other side went away - mark port in RESET state
3263af08d82Slm66018 */
327a8ea4edeSnarayan vport->status = VLDC_PORT_RESET;
3283af08d82Slm66018 vport->hanged_up = B_TRUE;
3293af08d82Slm66018 pollevents = POLLHUP;
3301ae08745Sheppo }
3311ae08745Sheppo
3321ae08745Sheppo if (event & LDC_EVT_READ)
3331ae08745Sheppo pollevents |= POLLIN;
3341ae08745Sheppo
335cb112a14Slm66018 mutex_exit(&vport->minorp->lock);
336cb112a14Slm66018
3371ae08745Sheppo if (pollevents != 0) {
3381ae08745Sheppo D1("i_vldc_cb: port@%d pollwakeup=0x%x\n",
3391ae08745Sheppo vport->number, pollevents);
3401ae08745Sheppo pollwakeup(&vport->poll, pollevents);
3411ae08745Sheppo }
3421ae08745Sheppo
3431ae08745Sheppo return (LDC_SUCCESS);
3441ae08745Sheppo }
3451ae08745Sheppo
3461ae08745Sheppo /* mdeg callback */
3471ae08745Sheppo static int
i_vldc_mdeg_cb(void * cb_argp,mdeg_result_t * resp)3481ae08745Sheppo i_vldc_mdeg_cb(void *cb_argp, mdeg_result_t *resp)
3491ae08745Sheppo {
3501ae08745Sheppo vldc_t *vldcp;
3511ae08745Sheppo int idx;
3521ae08745Sheppo uint64_t portno;
3531ae08745Sheppo int rv;
3541ae08745Sheppo md_t *mdp;
3551ae08745Sheppo mde_cookie_t node;
3561ae08745Sheppo
3571ae08745Sheppo if (resp == NULL) {
3581ae08745Sheppo D1("i_vldc_mdeg_cb: no result returned\n");
3591ae08745Sheppo return (MDEG_FAILURE);
3601ae08745Sheppo }
3611ae08745Sheppo
3621ae08745Sheppo vldcp = (vldc_t *)cb_argp;
3631ae08745Sheppo
3641ae08745Sheppo mutex_enter(&vldcp->lock);
3651ae08745Sheppo if (vldcp->detaching == B_TRUE) {
3661ae08745Sheppo D1("i_vldc_mdeg_cb: detach in progress\n");
3671ae08745Sheppo mutex_exit(&vldcp->lock);
3681ae08745Sheppo return (MDEG_FAILURE);
3691ae08745Sheppo }
3701ae08745Sheppo
3711ae08745Sheppo D1("i_vldc_mdeg_cb: added=%d, removed=%d, matched=%d\n",
3721ae08745Sheppo resp->added.nelem, resp->removed.nelem, resp->match_prev.nelem);
3731ae08745Sheppo
3741ae08745Sheppo /* process added ports */
3751ae08745Sheppo for (idx = 0; idx < resp->added.nelem; idx++) {
3761ae08745Sheppo mdp = resp->added.mdp;
3771ae08745Sheppo node = resp->added.mdep[idx];
3781ae08745Sheppo
3791ae08745Sheppo D1("i_vldc_mdeg_cb: processing added node 0x%lx\n", node);
3801ae08745Sheppo
3811ae08745Sheppo /* attempt to add a port */
3821ae08745Sheppo if ((rv = i_vldc_add_port(vldcp, mdp, node)) != MDEG_SUCCESS) {
3831ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_mdeg_cb: unable to add port, "
3841ae08745Sheppo "err = %d", rv);
3851ae08745Sheppo }
3861ae08745Sheppo }
3871ae08745Sheppo
3881ae08745Sheppo /* process removed ports */
3891ae08745Sheppo for (idx = 0; idx < resp->removed.nelem; idx++) {
3901ae08745Sheppo mdp = resp->removed.mdp;
3911ae08745Sheppo node = resp->removed.mdep[idx];
3921ae08745Sheppo
3931ae08745Sheppo D1("i_vldc_mdeg_cb: processing removed node 0x%lx\n", node);
3941ae08745Sheppo
3951ae08745Sheppo /* read in the port's id property */
3961ae08745Sheppo if (md_get_prop_val(mdp, node, "id", &portno)) {
3971ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_mdeg_cb: node 0x%lx of "
3981ae08745Sheppo "removed list has no 'id' property", node);
3991ae08745Sheppo continue;
4001ae08745Sheppo }
4011ae08745Sheppo
4021ae08745Sheppo /* attempt to remove a port */
4031ae08745Sheppo if ((rv = i_vldc_remove_port(vldcp, portno)) != 0) {
4041ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_mdeg_cb: unable to remove "
4051ae08745Sheppo "port %lu, err %d", portno, rv);
4061ae08745Sheppo }
4071ae08745Sheppo }
4081ae08745Sheppo
4091ae08745Sheppo /*
4101ae08745Sheppo * Currently no support for updating already active ports. So, ignore
4111ae08745Sheppo * the match_curr and match_prev arrays for now.
4121ae08745Sheppo */
4131ae08745Sheppo
4141ae08745Sheppo mutex_exit(&vldcp->lock);
4151ae08745Sheppo
4161ae08745Sheppo return (MDEG_SUCCESS);
4171ae08745Sheppo }
4181ae08745Sheppo
4191ae08745Sheppo /* register callback to mdeg */
4201ae08745Sheppo static int
i_vldc_mdeg_register(vldc_t * vldcp)4211ae08745Sheppo i_vldc_mdeg_register(vldc_t *vldcp)
4221ae08745Sheppo {
4231ae08745Sheppo mdeg_prop_spec_t *pspecp;
4241ae08745Sheppo mdeg_node_spec_t *inst_specp;
4251ae08745Sheppo mdeg_handle_t mdeg_hdl;
4261ae08745Sheppo size_t templatesz;
4271ae08745Sheppo int inst;
4281ae08745Sheppo char *name;
4291ae08745Sheppo size_t namesz;
4301ae08745Sheppo char *nameprop;
4311ae08745Sheppo int rv;
4321ae08745Sheppo
4331ae08745Sheppo /* get the unique vldc instance assigned by the LDom manager */
4341ae08745Sheppo inst = ddi_prop_get_int(DDI_DEV_T_ANY, vldcp->dip,
4351ae08745Sheppo DDI_PROP_DONTPASS, "reg", -1);
4361ae08745Sheppo if (inst == -1) {
4371ae08745Sheppo cmn_err(CE_NOTE, "?vldc%d has no 'reg' property",
4381ae08745Sheppo ddi_get_instance(vldcp->dip));
4391ae08745Sheppo return (DDI_FAILURE);
4401ae08745Sheppo }
4411ae08745Sheppo
4421ae08745Sheppo /* get the name of the vldc instance */
4431ae08745Sheppo rv = ddi_prop_lookup_string(DDI_DEV_T_ANY, vldcp->dip,
4441ae08745Sheppo DDI_PROP_DONTPASS, "name", &nameprop);
4451ae08745Sheppo if (rv != DDI_PROP_SUCCESS) {
4461ae08745Sheppo cmn_err(CE_NOTE, "?vldc%d has no 'name' property",
4471ae08745Sheppo ddi_get_instance(vldcp->dip));
4481ae08745Sheppo return (DDI_FAILURE);
4491ae08745Sheppo }
4501ae08745Sheppo
4511ae08745Sheppo D1("i_vldc_mdeg_register: name=%s, instance=%d\n", nameprop, inst);
4521ae08745Sheppo
4531ae08745Sheppo /*
4541ae08745Sheppo * Allocate and initialize a per-instance copy
4551ae08745Sheppo * of the global property spec array that will
4561ae08745Sheppo * uniquely identify this vldc instance.
4571ae08745Sheppo */
4581ae08745Sheppo templatesz = sizeof (vldc_prop_template);
4591ae08745Sheppo pspecp = kmem_alloc(templatesz, KM_SLEEP);
4601ae08745Sheppo
4611ae08745Sheppo bcopy(vldc_prop_template, pspecp, templatesz);
4621ae08745Sheppo
4631ae08745Sheppo /* copy in the name property */
4641ae08745Sheppo namesz = strlen(nameprop) + 1;
4651ae08745Sheppo name = kmem_alloc(namesz, KM_SLEEP);
4661ae08745Sheppo
4671ae08745Sheppo bcopy(nameprop, name, namesz);
4681ae08745Sheppo VLDC_SET_MDEG_PROP_NAME(pspecp, name);
469d10e4ef2Snarayan ddi_prop_free(nameprop);
4701ae08745Sheppo
4711ae08745Sheppo /* copy in the instance property */
4721ae08745Sheppo VLDC_SET_MDEG_PROP_INST(pspecp, inst);
4731ae08745Sheppo
4741ae08745Sheppo /* initialize the complete prop spec structure */
4751ae08745Sheppo inst_specp = kmem_alloc(sizeof (mdeg_node_spec_t), KM_SLEEP);
4761ae08745Sheppo inst_specp->namep = "virtual-device";
4771ae08745Sheppo inst_specp->specp = pspecp;
4781ae08745Sheppo
4791ae08745Sheppo /* perform the registration */
4801ae08745Sheppo rv = mdeg_register(inst_specp, &vport_match, i_vldc_mdeg_cb,
4811ae08745Sheppo vldcp, &mdeg_hdl);
4821ae08745Sheppo
4831ae08745Sheppo if (rv != MDEG_SUCCESS) {
4841ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_mdeg_register: mdeg_register "
4851ae08745Sheppo "failed, err = %d", rv);
4861ae08745Sheppo kmem_free(name, namesz);
4871ae08745Sheppo kmem_free(pspecp, templatesz);
4881ae08745Sheppo kmem_free(inst_specp, sizeof (mdeg_node_spec_t));
4891ae08745Sheppo return (DDI_FAILURE);
4901ae08745Sheppo }
4911ae08745Sheppo
4921ae08745Sheppo /* save off data that will be needed later */
4931ae08745Sheppo vldcp->inst_spec = inst_specp;
4941ae08745Sheppo vldcp->mdeg_hdl = mdeg_hdl;
4951ae08745Sheppo
4961ae08745Sheppo return (DDI_SUCCESS);
4971ae08745Sheppo }
4981ae08745Sheppo
4991ae08745Sheppo /* unregister callback from mdeg */
5001ae08745Sheppo static int
i_vldc_mdeg_unregister(vldc_t * vldcp)5011ae08745Sheppo i_vldc_mdeg_unregister(vldc_t *vldcp)
5021ae08745Sheppo {
5031ae08745Sheppo char *name;
5041ae08745Sheppo int rv;
5051ae08745Sheppo
5061ae08745Sheppo D1("i_vldc_mdeg_unregister: hdl=0x%lx\n", vldcp->mdeg_hdl);
5071ae08745Sheppo
5081ae08745Sheppo rv = mdeg_unregister(vldcp->mdeg_hdl);
5091ae08745Sheppo if (rv != MDEG_SUCCESS) {
5101ae08745Sheppo return (rv);
5111ae08745Sheppo }
5121ae08745Sheppo
5131ae08745Sheppo /*
5141ae08745Sheppo * Clean up cached MDEG data
5151ae08745Sheppo */
5161ae08745Sheppo name = VLDC_MDEG_PROP_NAME(vldcp->inst_spec->specp);
5171ae08745Sheppo if (name != NULL) {
5181ae08745Sheppo kmem_free(name, strlen(name) + 1);
5191ae08745Sheppo }
5201ae08745Sheppo kmem_free(vldcp->inst_spec->specp, sizeof (vldc_prop_template));
5211ae08745Sheppo vldcp->inst_spec->specp = NULL;
5221ae08745Sheppo
5231ae08745Sheppo kmem_free(vldcp->inst_spec, sizeof (mdeg_node_spec_t));
5241ae08745Sheppo vldcp->inst_spec = NULL;
5251ae08745Sheppo
5261ae08745Sheppo return (MDEG_SUCCESS);
5271ae08745Sheppo }
5281ae08745Sheppo
5291ae08745Sheppo static int
i_vldc_get_port_channel(md_t * mdp,mde_cookie_t node,uint64_t * ldc_id)5301ae08745Sheppo i_vldc_get_port_channel(md_t *mdp, mde_cookie_t node, uint64_t *ldc_id)
5311ae08745Sheppo {
5321ae08745Sheppo int num_nodes, nchan;
5331ae08745Sheppo size_t listsz;
5341ae08745Sheppo mde_cookie_t *listp;
5351ae08745Sheppo
5361ae08745Sheppo /*
5371ae08745Sheppo * Find the channel-endpoint node(s) (which should be under this
5381ae08745Sheppo * port node) which contain the channel id(s).
5391ae08745Sheppo */
5401ae08745Sheppo if ((num_nodes = md_node_count(mdp)) <= 0) {
5411ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_get_port_channel: invalid number of "
5421ae08745Sheppo "channel-endpoint nodes found (%d)", num_nodes);
5431ae08745Sheppo return (-1);
5441ae08745Sheppo }
5451ae08745Sheppo
5461ae08745Sheppo /* allocate space for node list */
5471ae08745Sheppo listsz = num_nodes * sizeof (mde_cookie_t);
5481ae08745Sheppo listp = kmem_alloc(listsz, KM_SLEEP);
5491ae08745Sheppo
5501ae08745Sheppo nchan = md_scan_dag(mdp, node, md_find_name(mdp, "channel-endpoint"),
5511ae08745Sheppo md_find_name(mdp, "fwd"), listp);
5521ae08745Sheppo
5531ae08745Sheppo if (nchan <= 0) {
5541ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_get_port_channel: no channel-endpoint"
5551ae08745Sheppo " nodes found");
5561ae08745Sheppo kmem_free(listp, listsz);
5571ae08745Sheppo return (-1);
5581ae08745Sheppo }
5591ae08745Sheppo
5601ae08745Sheppo D2("i_vldc_get_port_channel: %d channel-endpoint nodes found", nchan);
5611ae08745Sheppo
5621ae08745Sheppo /* use property from first node found */
5631ae08745Sheppo if (md_get_prop_val(mdp, listp[0], "id", ldc_id)) {
5641ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_get_port_channel: channel-endpoint "
5651ae08745Sheppo "has no 'id' property");
5661ae08745Sheppo kmem_free(listp, listsz);
5671ae08745Sheppo return (-1);
5681ae08745Sheppo }
5691ae08745Sheppo
5701ae08745Sheppo kmem_free(listp, listsz);
5711ae08745Sheppo
5721ae08745Sheppo return (0);
5731ae08745Sheppo }
5741ae08745Sheppo
5751ae08745Sheppo /* add a vldc port */
5761ae08745Sheppo static int
i_vldc_add_port(vldc_t * vldcp,md_t * mdp,mde_cookie_t node)5771ae08745Sheppo i_vldc_add_port(vldc_t *vldcp, md_t *mdp, mde_cookie_t node)
5781ae08745Sheppo {
5791ae08745Sheppo vldc_port_t *vport;
5801ae08745Sheppo char *sname;
5811ae08745Sheppo uint64_t portno;
5821ae08745Sheppo int vldc_inst;
5831ae08745Sheppo minor_t minor;
5841ae08745Sheppo int minor_idx;
5851ae08745Sheppo boolean_t new_minor;
5861ae08745Sheppo int rv;
5871ae08745Sheppo
588cb112a14Slm66018 ASSERT(MUTEX_HELD(&vldcp->lock));
589cb112a14Slm66018
5901ae08745Sheppo /* read in the port's id property */
5911ae08745Sheppo if (md_get_prop_val(mdp, node, "id", &portno)) {
5921ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_add_port: node 0x%lx of added "
5931ae08745Sheppo "list has no 'id' property", node);
5941ae08745Sheppo return (MDEG_FAILURE);
5951ae08745Sheppo }
5961ae08745Sheppo
5971ae08745Sheppo if (portno >= VLDC_MAX_PORTS) {
5981ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_add_port: found port number (%lu) "
5991ae08745Sheppo "larger than maximum supported number of ports", portno);
6001ae08745Sheppo return (MDEG_FAILURE);
6011ae08745Sheppo }
6021ae08745Sheppo
6031ae08745Sheppo vport = &(vldcp->port[portno]);
6041ae08745Sheppo
6051ae08745Sheppo if (vport->minorp != NULL) {
6061ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_add_port: trying to add a port (%lu)"
6071ae08745Sheppo " which is already bound", portno);
6081ae08745Sheppo return (MDEG_FAILURE);
6091ae08745Sheppo }
6101ae08745Sheppo
6111ae08745Sheppo vport->number = portno;
6121ae08745Sheppo
6131ae08745Sheppo /* get all channels for this device (currently only one) */
6141ae08745Sheppo if (i_vldc_get_port_channel(mdp, node, &vport->ldc_id) == -1) {
6151ae08745Sheppo return (MDEG_FAILURE);
6161ae08745Sheppo }
6171ae08745Sheppo
6181ae08745Sheppo /* set the default MTU */
619d1a9c4c1Sjm22469 vport->mtu = VLDC_DEFAULT_MTU;
6201ae08745Sheppo
6211ae08745Sheppo /* get the service being exported by this port */
6221ae08745Sheppo if (md_get_prop_str(mdp, node, "vldc-svc-name", &sname)) {
6231ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_add_port: vdevice has no "
6241ae08745Sheppo "'vldc-svc-name' property");
6251ae08745Sheppo return (MDEG_FAILURE);
6261ae08745Sheppo }
6271ae08745Sheppo
6281ae08745Sheppo /* minor number look up */
6291ae08745Sheppo for (minor_idx = 0; minor_idx < vldcp->minors_assigned;
6301ae08745Sheppo minor_idx++) {
6311ae08745Sheppo if (strcmp(vldcp->minor_tbl[minor_idx].sname, sname) == 0) {
6321ae08745Sheppo /* found previously assigned minor number */
6331ae08745Sheppo break;
6341ae08745Sheppo }
6351ae08745Sheppo }
6361ae08745Sheppo
6371ae08745Sheppo new_minor = B_FALSE;
6381ae08745Sheppo if (minor_idx == vldcp->minors_assigned) {
6391ae08745Sheppo /* end of lookup - assign new minor number */
6401ae08745Sheppo if (vldcp->minors_assigned == VLDC_MAX_MINORS) {
6411ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_add_port: too many minor "
6421ae08745Sheppo "nodes (%d)", minor_idx);
6431ae08745Sheppo return (MDEG_FAILURE);
6441ae08745Sheppo }
6451ae08745Sheppo
6461ae08745Sheppo (void) strlcpy(vldcp->minor_tbl[minor_idx].sname,
6471ae08745Sheppo sname, MAXPATHLEN);
6481ae08745Sheppo
6491ae08745Sheppo vldcp->minors_assigned++;
6501ae08745Sheppo new_minor = B_TRUE;
6511ae08745Sheppo }
6521ae08745Sheppo
653cb112a14Slm66018 if (vldcp->minor_tbl[minor_idx].portno != VLDC_INVALID_PORTNO) {
654cb112a14Slm66018 cmn_err(CE_NOTE, "?i_vldc_add_port: trying to add a port (%lu)"
655cb112a14Slm66018 " which has a minor number in use by port (%u)",
656cb112a14Slm66018 portno, vldcp->minor_tbl[minor_idx].portno);
657cb112a14Slm66018 return (MDEG_FAILURE);
658cb112a14Slm66018 }
6591ae08745Sheppo
6603af08d82Slm66018 vldc_inst = ddi_get_instance(vldcp->dip);
6613af08d82Slm66018
6623af08d82Slm66018 vport->inst = vldc_inst;
6631ae08745Sheppo vport->minorp = &vldcp->minor_tbl[minor_idx];
6641ae08745Sheppo vldcp->minor_tbl[minor_idx].portno = portno;
6651ae08745Sheppo vldcp->minor_tbl[minor_idx].in_use = 0;
6661ae08745Sheppo
6673af08d82Slm66018 D1("i_vldc_add_port: vldc@%d:%d mtu=%d, ldc=%ld, service=%s\n",
6683af08d82Slm66018 vport->inst, vport->number, vport->mtu, vport->ldc_id, sname);
6691ae08745Sheppo
6701ae08745Sheppo /*
6711ae08745Sheppo * Create a minor node. The minor number is
6721ae08745Sheppo * (vldc_inst << VLDC_INST_SHIFT) | minor_idx
6731ae08745Sheppo */
6741ae08745Sheppo minor = (vldc_inst << VLDC_INST_SHIFT) | (minor_idx);
6751ae08745Sheppo
6761ae08745Sheppo rv = ddi_create_minor_node(vldcp->dip, sname, S_IFCHR,
6771ae08745Sheppo minor, DDI_NT_SERIAL, 0);
6781ae08745Sheppo
6791ae08745Sheppo if (rv != DDI_SUCCESS) {
6801ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_add_port: failed to create minor"
6811ae08745Sheppo "node (%u), err = %d", minor, rv);
6821ae08745Sheppo vldcp->minor_tbl[minor_idx].portno = VLDC_INVALID_PORTNO;
6831ae08745Sheppo if (new_minor) {
6841ae08745Sheppo vldcp->minors_assigned--;
6851ae08745Sheppo }
6861ae08745Sheppo return (MDEG_FAILURE);
6871ae08745Sheppo }
6881ae08745Sheppo
6891ae08745Sheppo /*
6901ae08745Sheppo * The port is now bound to a minor node and is initially in the
6911ae08745Sheppo * closed state.
6921ae08745Sheppo */
6931ae08745Sheppo vport->status = VLDC_PORT_CLOSED;
6941ae08745Sheppo
6951ae08745Sheppo D1("i_vldc_add_port: port %lu initialized\n", portno);
6961ae08745Sheppo
6971ae08745Sheppo return (MDEG_SUCCESS);
6981ae08745Sheppo }
6991ae08745Sheppo
7001ae08745Sheppo /* remove a vldc port */
7011ae08745Sheppo static int
i_vldc_remove_port(vldc_t * vldcp,uint_t portno)7021ae08745Sheppo i_vldc_remove_port(vldc_t *vldcp, uint_t portno)
7031ae08745Sheppo {
7041ae08745Sheppo vldc_port_t *vport;
7051ae08745Sheppo vldc_minor_t *vminor;
7061ae08745Sheppo
707cb112a14Slm66018 ASSERT(vldcp != NULL);
708cb112a14Slm66018 ASSERT(MUTEX_HELD(&vldcp->lock));
709cb112a14Slm66018
7101ae08745Sheppo vport = &(vldcp->port[portno]);
7111ae08745Sheppo vminor = vport->minorp;
7121ae08745Sheppo if (vminor == NULL) {
7131ae08745Sheppo cmn_err(CE_NOTE, "?i_vldc_remove_port: trying to remove a "
7141ae08745Sheppo "port (%u) which is not bound", portno);
7151ae08745Sheppo return (MDEG_FAILURE);
7161ae08745Sheppo }
7171ae08745Sheppo
7181ae08745Sheppo /*
7191ae08745Sheppo * Make sure that all new attempts to open or use the minor node
7201ae08745Sheppo * associated with the port will fail.
7211ae08745Sheppo */
7221ae08745Sheppo mutex_enter(&vminor->lock);
7231ae08745Sheppo vminor->portno = VLDC_INVALID_PORTNO;
7241ae08745Sheppo mutex_exit(&vminor->lock);
7251ae08745Sheppo
7261ae08745Sheppo /* send hangup to anyone polling */
7271ae08745Sheppo pollwakeup(&vport->poll, POLLHUP);
7281ae08745Sheppo
7291ae08745Sheppo /* Now wait for all current users of the minor node to finish. */
7301ae08745Sheppo mutex_enter(&vminor->lock);
7311ae08745Sheppo while (vminor->in_use > 0) {
7321ae08745Sheppo cv_wait(&vminor->cv, &vminor->lock);
7331ae08745Sheppo }
7341ae08745Sheppo
7353af08d82Slm66018 if (vport->status != VLDC_PORT_CLOSED) {
7361ae08745Sheppo /* close the port before it is torn down */
7371ae08745Sheppo (void) i_vldc_close_port(vldcp, portno);
7381ae08745Sheppo }
7391ae08745Sheppo
7401ae08745Sheppo /* remove minor node */
7411ae08745Sheppo ddi_remove_minor_node(vldcp->dip, vport->minorp->sname);
7421ae08745Sheppo vport->minorp = NULL;
7431ae08745Sheppo
7441ae08745Sheppo mutex_exit(&vminor->lock);
7451ae08745Sheppo
7461ae08745Sheppo D1("i_vldc_remove_port: removed vldc port %u\n", portno);
7471ae08745Sheppo
7481ae08745Sheppo return (MDEG_SUCCESS);
7491ae08745Sheppo }
7501ae08745Sheppo
751cb112a14Slm66018 /*
752cb112a14Slm66018 * Close and destroy the ldc channel associated with the port 'vport'
753cb112a14Slm66018 *
754cb112a14Slm66018 * NOTE It may not be possible close and destroy the channel if resources
755cb112a14Slm66018 * are still in use so the fucntion may exit before all the teardown
756cb112a14Slm66018 * operations are completed and would have to be called again by the
757cb112a14Slm66018 * vldc framework.
758cb112a14Slm66018 *
759cb112a14Slm66018 * This function needs to be able to handle the case where it is called
760cb112a14Slm66018 * more than once and has to pick up from where it left off.
761cb112a14Slm66018 */
7621ae08745Sheppo static int
i_vldc_ldc_close(vldc_port_t * vport)7631ae08745Sheppo i_vldc_ldc_close(vldc_port_t *vport)
7641ae08745Sheppo {
765cb112a14Slm66018 int err = 0;
7661ae08745Sheppo
767cb112a14Slm66018 ASSERT(MUTEX_HELD(&vport->minorp->lock));
768cb112a14Slm66018
769cb112a14Slm66018 /*
770cb112a14Slm66018 * If ldc_close() succeeded or if the channel was already closed[*]
771cb112a14Slm66018 * (possibly by a previously unsuccessful call to this function)
772cb112a14Slm66018 * we keep going and try to teardown the rest of the LDC state,
773cb112a14Slm66018 * otherwise we bail out.
774cb112a14Slm66018 *
775cb112a14Slm66018 * [*] indicated by ldc_close() returning a value of EFAULT
776cb112a14Slm66018 */
7774d39be2bSsg70180 err = ldc_close(vport->ldc_handle);
778cb112a14Slm66018 if ((err != 0) && (err != EFAULT))
779cb112a14Slm66018 return (err);
780cb112a14Slm66018
7811ae08745Sheppo err = ldc_unreg_callback(vport->ldc_handle);
782cb112a14Slm66018 if (err != 0)
783cb112a14Slm66018 return (err);
784cb112a14Slm66018
7851ae08745Sheppo err = ldc_fini(vport->ldc_handle);
786cb112a14Slm66018 if (err != 0)
787cb112a14Slm66018 return (err);
7881ae08745Sheppo
7893af08d82Slm66018 vport->status = VLDC_PORT_OPEN;
7903af08d82Slm66018
791cb112a14Slm66018 return (0);
7921ae08745Sheppo }
7931ae08745Sheppo
7941ae08745Sheppo /* close a vldc port */
7951ae08745Sheppo static int
i_vldc_close_port(vldc_t * vldcp,uint_t portno)7961ae08745Sheppo i_vldc_close_port(vldc_t *vldcp, uint_t portno)
7971ae08745Sheppo {
7981ae08745Sheppo vldc_port_t *vport;
7994d39be2bSsg70180 vldc_minor_t *vminor;
8003af08d82Slm66018 int rv = DDI_SUCCESS;
8011ae08745Sheppo
8021ae08745Sheppo vport = &(vldcp->port[portno]);
8031ae08745Sheppo
8041ae08745Sheppo ASSERT(MUTEX_HELD(&vport->minorp->lock));
8051ae08745Sheppo
8063af08d82Slm66018 D1("i_vldc_close_port: vldc@%d:%d: closing port\n",
8073af08d82Slm66018 vport->inst, vport->minorp->portno);
8083af08d82Slm66018
8094d39be2bSsg70180 vminor = vport->minorp;
8104d39be2bSsg70180
8113af08d82Slm66018 switch (vport->status) {
8123af08d82Slm66018 case VLDC_PORT_CLOSED:
8131ae08745Sheppo /* nothing to do */
8141ae08745Sheppo DWARN("i_vldc_close_port: port %d in an unexpected "
8151ae08745Sheppo "state (%d)\n", portno, vport->status);
8161ae08745Sheppo return (DDI_SUCCESS);
8173af08d82Slm66018
8183af08d82Slm66018 case VLDC_PORT_READY:
8193af08d82Slm66018 case VLDC_PORT_RESET:
8204d39be2bSsg70180 do {
8213af08d82Slm66018 rv = i_vldc_ldc_close(vport);
8224d39be2bSsg70180 if (rv != EAGAIN)
8234d39be2bSsg70180 break;
8244d39be2bSsg70180
8254d39be2bSsg70180 /*
8264d39be2bSsg70180 * EAGAIN indicates that ldc_close() failed because
8274d39be2bSsg70180 * ldc callback thread is active for the channel.
8284d39be2bSsg70180 * cv_timedwait() is used to release vminor->lock and
8294d39be2bSsg70180 * allow ldc callback thread to complete.
8304d39be2bSsg70180 * after waking up, check if the port has been closed
8314d39be2bSsg70180 * by another thread in the meantime.
8324d39be2bSsg70180 */
833*d3d50737SRafael Vanoni (void) cv_reltimedwait(&vminor->cv, &vminor->lock,
834*d3d50737SRafael Vanoni drv_usectohz(vldc_close_delay), TR_CLOCK_TICK);
8354d39be2bSsg70180 rv = 0;
8364d39be2bSsg70180 } while (vport->status != VLDC_PORT_CLOSED);
8374d39be2bSsg70180
8384d39be2bSsg70180 if ((rv != 0) || (vport->status == VLDC_PORT_CLOSED))
839cb112a14Slm66018 return (rv);
8404d39be2bSsg70180
8413af08d82Slm66018 break;
842cb112a14Slm66018
843cb112a14Slm66018 case VLDC_PORT_OPEN:
844cb112a14Slm66018 break;
845cb112a14Slm66018
846cb112a14Slm66018 default:
847cb112a14Slm66018 DWARN("i_vldc_close_port: port %d in an unexpected "
848cb112a14Slm66018 "state (%d)\n", portno, vport->status);
849cb112a14Slm66018 ASSERT(0); /* fail quickly to help diagnosis */
850cb112a14Slm66018 return (EINVAL);
8511ae08745Sheppo }
8521ae08745Sheppo
8531ae08745Sheppo ASSERT(vport->status == VLDC_PORT_OPEN);
8541ae08745Sheppo
8551ae08745Sheppo /* free memory */
8561ae08745Sheppo kmem_free(vport->send_buf, vport->mtu);
8571ae08745Sheppo kmem_free(vport->recv_buf, vport->mtu);
8581ae08745Sheppo
8594d39be2bSsg70180 if (strcmp(vminor->sname, VLDC_HVCTL_SVCNAME) == 0)
860d10e4ef2Snarayan kmem_free(vport->cookie_buf, vldc_max_cookie);
861d10e4ef2Snarayan
8621ae08745Sheppo vport->status = VLDC_PORT_CLOSED;
8631ae08745Sheppo
8641ae08745Sheppo return (rv);
8651ae08745Sheppo }
8661ae08745Sheppo
8671ae08745Sheppo /*
8681ae08745Sheppo * attach(9E): attach a device to the system.
8691ae08745Sheppo * called once for each instance of the device on the system.
8701ae08745Sheppo */
8711ae08745Sheppo static int
vldc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)8721ae08745Sheppo vldc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
8731ae08745Sheppo {
8741ae08745Sheppo int i, instance;
8751ae08745Sheppo vldc_t *vldcp;
8761ae08745Sheppo
8771ae08745Sheppo switch (cmd) {
8781ae08745Sheppo
8791ae08745Sheppo case DDI_ATTACH:
8801ae08745Sheppo
8811ae08745Sheppo instance = ddi_get_instance(dip);
8821ae08745Sheppo
8831ae08745Sheppo if (ddi_soft_state_zalloc(vldc_ssp, instance) != DDI_SUCCESS) {
8841ae08745Sheppo return (DDI_FAILURE);
8851ae08745Sheppo }
8861ae08745Sheppo
8871ae08745Sheppo vldcp = ddi_get_soft_state(vldc_ssp, instance);
8881ae08745Sheppo if (vldcp == NULL) {
8891ae08745Sheppo ddi_soft_state_free(vldc_ssp, instance);
8901ae08745Sheppo return (ENXIO);
8911ae08745Sheppo }
8921ae08745Sheppo
8931ae08745Sheppo D1("vldc_attach: DDI_ATTACH instance=%d\n", instance);
8941ae08745Sheppo
8951ae08745Sheppo mutex_init(&vldcp->lock, NULL, MUTEX_DRIVER, NULL);
8961ae08745Sheppo vldcp->dip = dip;
8971ae08745Sheppo vldcp->detaching = B_FALSE;
8981ae08745Sheppo
8991ae08745Sheppo for (i = 0; i < VLDC_MAX_PORTS; i++) {
9001ae08745Sheppo /* No minor node association to start with */
9011ae08745Sheppo vldcp->port[i].minorp = NULL;
9021ae08745Sheppo }
9031ae08745Sheppo
9041ae08745Sheppo for (i = 0; i < VLDC_MAX_MINORS; i++) {
9051ae08745Sheppo mutex_init(&(vldcp->minor_tbl[i].lock), NULL,
9061ae08745Sheppo MUTEX_DRIVER, NULL);
9071ae08745Sheppo cv_init(&(vldcp->minor_tbl[i].cv), NULL,
9081ae08745Sheppo CV_DRIVER, NULL);
9091ae08745Sheppo /* No port association to start with */
9101ae08745Sheppo vldcp->minor_tbl[i].portno = VLDC_INVALID_PORTNO;
9111ae08745Sheppo }
9121ae08745Sheppo
9131ae08745Sheppo /* Register for MD update notification */
9141ae08745Sheppo if (i_vldc_mdeg_register(vldcp) != DDI_SUCCESS) {
9151ae08745Sheppo ddi_soft_state_free(vldc_ssp, instance);
9161ae08745Sheppo return (DDI_FAILURE);
9171ae08745Sheppo }
9181ae08745Sheppo
9191ae08745Sheppo return (DDI_SUCCESS);
9201ae08745Sheppo
9211ae08745Sheppo case DDI_RESUME:
9221ae08745Sheppo
9231ae08745Sheppo return (DDI_SUCCESS);
9241ae08745Sheppo
9251ae08745Sheppo default:
9261ae08745Sheppo
9271ae08745Sheppo return (DDI_FAILURE);
9281ae08745Sheppo }
9291ae08745Sheppo }
9301ae08745Sheppo
9311ae08745Sheppo /*
9321ae08745Sheppo * detach(9E): detach a device from the system.
9331ae08745Sheppo */
9341ae08745Sheppo static int
vldc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)9351ae08745Sheppo vldc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
9361ae08745Sheppo {
9371ae08745Sheppo int i, instance;
9381ae08745Sheppo vldc_t *vldcp;
9391ae08745Sheppo
9401ae08745Sheppo switch (cmd) {
9411ae08745Sheppo
9421ae08745Sheppo case DDI_DETACH:
9431ae08745Sheppo
9441ae08745Sheppo instance = ddi_get_instance(dip);
9451ae08745Sheppo
9461ae08745Sheppo vldcp = ddi_get_soft_state(vldc_ssp, instance);
9471ae08745Sheppo if (vldcp == NULL) {
9481ae08745Sheppo return (DDI_FAILURE);
9491ae08745Sheppo }
9501ae08745Sheppo
9511ae08745Sheppo D1("vldc_detach: DDI_DETACH instance=%d\n", instance);
9521ae08745Sheppo
9531ae08745Sheppo mutex_enter(&vldcp->lock);
9541ae08745Sheppo
9551ae08745Sheppo /* Fail the detach if all ports have not been removed. */
9561ae08745Sheppo for (i = 0; i < VLDC_MAX_MINORS; i++) {
9571ae08745Sheppo if (vldcp->minor_tbl[i].portno != VLDC_INVALID_PORTNO) {
9581ae08745Sheppo D1("vldc_detach: vldc@%d:%d is bound, "
9591ae08745Sheppo "detach failed\n",
9601ae08745Sheppo instance, vldcp->minor_tbl[i].portno);
9611ae08745Sheppo mutex_exit(&vldcp->lock);
9621ae08745Sheppo return (DDI_FAILURE);
9631ae08745Sheppo }
9641ae08745Sheppo }
9651ae08745Sheppo
9661ae08745Sheppo /*
9671ae08745Sheppo * Prevent MDEG from adding new ports before the callback can
9681ae08745Sheppo * be unregistered. The lock can't be held accross the
9691ae08745Sheppo * unregistration call because a callback may be in progress
9701ae08745Sheppo * and blocked on the lock.
9711ae08745Sheppo */
9721ae08745Sheppo vldcp->detaching = B_TRUE;
9731ae08745Sheppo
9741ae08745Sheppo mutex_exit(&vldcp->lock);
9751ae08745Sheppo
9761ae08745Sheppo if (i_vldc_mdeg_unregister(vldcp) != MDEG_SUCCESS) {
9771ae08745Sheppo vldcp->detaching = B_FALSE;
9781ae08745Sheppo return (DDI_FAILURE);
9791ae08745Sheppo }
9801ae08745Sheppo
9811ae08745Sheppo /* Tear down all bound ports and free resources. */
9821ae08745Sheppo for (i = 0; i < VLDC_MAX_MINORS; i++) {
9831ae08745Sheppo if (vldcp->minor_tbl[i].portno != VLDC_INVALID_PORTNO) {
9841ae08745Sheppo (void) i_vldc_remove_port(vldcp, i);
9851ae08745Sheppo }
9861ae08745Sheppo mutex_destroy(&(vldcp->minor_tbl[i].lock));
9871ae08745Sheppo cv_destroy(&(vldcp->minor_tbl[i].cv));
9881ae08745Sheppo }
9891ae08745Sheppo
9901ae08745Sheppo mutex_destroy(&vldcp->lock);
9911ae08745Sheppo ddi_soft_state_free(vldc_ssp, instance);
9921ae08745Sheppo
9931ae08745Sheppo return (DDI_SUCCESS);
9941ae08745Sheppo
9951ae08745Sheppo case DDI_SUSPEND:
9961ae08745Sheppo
9971ae08745Sheppo return (DDI_SUCCESS);
9981ae08745Sheppo
9991ae08745Sheppo default:
10001ae08745Sheppo
10011ae08745Sheppo return (DDI_FAILURE);
10021ae08745Sheppo }
10031ae08745Sheppo }
10041ae08745Sheppo
10051ae08745Sheppo /* cb_open */
10061ae08745Sheppo static int
vldc_open(dev_t * devp,int flag,int otyp,cred_t * cred)10071ae08745Sheppo vldc_open(dev_t *devp, int flag, int otyp, cred_t *cred)
10081ae08745Sheppo {
10091ae08745Sheppo _NOTE(ARGUNUSED(flag, otyp, cred))
10101ae08745Sheppo
10111ae08745Sheppo int instance;
10121ae08745Sheppo minor_t minor;
10131ae08745Sheppo uint64_t portno;
10141ae08745Sheppo vldc_t *vldcp;
10151ae08745Sheppo vldc_port_t *vport;
10161ae08745Sheppo vldc_minor_t *vminor;
10171ae08745Sheppo
10181ae08745Sheppo minor = getminor(*devp);
10191ae08745Sheppo instance = VLDCINST(minor);
10201ae08745Sheppo vldcp = ddi_get_soft_state(vldc_ssp, instance);
10211ae08745Sheppo if (vldcp == NULL)
10221ae08745Sheppo return (ENXIO);
10231ae08745Sheppo
10241ae08745Sheppo vminor = VLDCMINOR(vldcp, minor);
10251ae08745Sheppo mutex_enter(&vminor->lock);
10261ae08745Sheppo portno = vminor->portno;
10271ae08745Sheppo if (portno == VLDC_INVALID_PORTNO) {
10281ae08745Sheppo mutex_exit(&vminor->lock);
10291ae08745Sheppo return (ENXIO);
10301ae08745Sheppo }
10311ae08745Sheppo
10321ae08745Sheppo vport = &(vldcp->port[portno]);
10331ae08745Sheppo
10341ae08745Sheppo D1("vldc_open: opening vldc@%d:%lu\n", instance, portno);
10351ae08745Sheppo
10361ae08745Sheppo if (vport->status != VLDC_PORT_CLOSED) {
10371ae08745Sheppo mutex_exit(&vminor->lock);
10381ae08745Sheppo return (EBUSY);
10391ae08745Sheppo }
10401ae08745Sheppo
10411ae08745Sheppo vport->recv_buf = kmem_alloc(vport->mtu, KM_SLEEP);
10421ae08745Sheppo vport->send_buf = kmem_alloc(vport->mtu, KM_SLEEP);
10431ae08745Sheppo
1044d10e4ef2Snarayan if (strcmp(vport->minorp->sname, VLDC_HVCTL_SVCNAME) == 0)
1045d10e4ef2Snarayan vport->cookie_buf = kmem_alloc(vldc_max_cookie, KM_SLEEP);
1046d10e4ef2Snarayan
10471ae08745Sheppo vport->is_stream = B_FALSE; /* assume not a stream */
10481ae08745Sheppo vport->hanged_up = B_FALSE;
10491ae08745Sheppo
10501ae08745Sheppo vport->status = VLDC_PORT_OPEN;
10511ae08745Sheppo
10521ae08745Sheppo mutex_exit(&vminor->lock);
10531ae08745Sheppo
10541ae08745Sheppo return (DDI_SUCCESS);
10551ae08745Sheppo }
10561ae08745Sheppo
10571ae08745Sheppo /* cb_close */
10581ae08745Sheppo static int
vldc_close(dev_t dev,int flag,int otyp,cred_t * cred)10591ae08745Sheppo vldc_close(dev_t dev, int flag, int otyp, cred_t *cred)
10601ae08745Sheppo {
10611ae08745Sheppo _NOTE(ARGUNUSED(flag, otyp, cred))
10621ae08745Sheppo
10631ae08745Sheppo int instance;
10641ae08745Sheppo minor_t minor;
10651ae08745Sheppo uint64_t portno;
10661ae08745Sheppo vldc_t *vldcp;
10671ae08745Sheppo vldc_minor_t *vminor;
10681ae08745Sheppo int rv;
10691ae08745Sheppo
10701ae08745Sheppo minor = getminor(dev);
10711ae08745Sheppo instance = VLDCINST(minor);
10721ae08745Sheppo vldcp = ddi_get_soft_state(vldc_ssp, instance);
10731ae08745Sheppo if (vldcp == NULL) {
10741ae08745Sheppo return (ENXIO);
10751ae08745Sheppo }
10761ae08745Sheppo
10771ae08745Sheppo vminor = VLDCMINOR(vldcp, minor);
10781ae08745Sheppo mutex_enter(&vminor->lock);
10791ae08745Sheppo portno = vminor->portno;
10801ae08745Sheppo if (portno == VLDC_INVALID_PORTNO) {
10811ae08745Sheppo mutex_exit(&vminor->lock);
10821ae08745Sheppo return (ENOLINK);
10831ae08745Sheppo }
10841ae08745Sheppo
10851ae08745Sheppo D1("vldc_close: closing vldc@%d:%lu\n", instance, portno);
10861ae08745Sheppo
10871ae08745Sheppo rv = i_vldc_close_port(vldcp, portno);
10881ae08745Sheppo
10891ae08745Sheppo mutex_exit(&vminor->lock);
10901ae08745Sheppo
10911ae08745Sheppo return (rv);
10921ae08745Sheppo }
10931ae08745Sheppo
10941ae08745Sheppo static int
vldc_set_ldc_mode(vldc_port_t * vport,vldc_t * vldcp,int channel_mode)10951ae08745Sheppo vldc_set_ldc_mode(vldc_port_t *vport, vldc_t *vldcp, int channel_mode)
10961ae08745Sheppo {
10971ae08745Sheppo ldc_attr_t attr;
10981ae08745Sheppo int rv;
10991ae08745Sheppo
11001ae08745Sheppo ASSERT(MUTEX_HELD(&vport->minorp->lock));
11011ae08745Sheppo
11021ae08745Sheppo /* validate mode */
11031ae08745Sheppo switch (channel_mode) {
110420ae46ebSha137994 case LDC_MODE_RELIABLE:
11051ae08745Sheppo vport->is_stream = B_TRUE;
11061ae08745Sheppo break;
11071ae08745Sheppo case LDC_MODE_RAW:
11081ae08745Sheppo case LDC_MODE_UNRELIABLE:
11091ae08745Sheppo vport->is_stream = B_FALSE;
11101ae08745Sheppo break;
11111ae08745Sheppo default:
11121ae08745Sheppo return (EINVAL);
11131ae08745Sheppo }
11141ae08745Sheppo
11151ae08745Sheppo if (vport->status == VLDC_PORT_READY) {
11161ae08745Sheppo rv = i_vldc_ldc_close(vport);
11171ae08745Sheppo if (rv != 0) {
11181ae08745Sheppo DWARN("vldc_set_ldc_mode: i_vldc_ldc_close "
11191ae08745Sheppo "failed, rv=%d\n", rv);
11201ae08745Sheppo return (rv);
11211ae08745Sheppo }
11221ae08745Sheppo }
11231ae08745Sheppo
11241ae08745Sheppo D1("vldc_set_ldc_mode: vport status %d, mode %d\n",
11251ae08745Sheppo vport->status, channel_mode);
11261ae08745Sheppo
11271ae08745Sheppo vport->ldc_mode = channel_mode;
11281ae08745Sheppo
11291ae08745Sheppo /* initialize the channel */
11301ae08745Sheppo attr.devclass = LDC_DEV_SERIAL;
11311ae08745Sheppo attr.instance = ddi_get_instance(vldcp->dip);
1132e1ebb9ecSlm66018 attr.mtu = vport->mtu;
11331ae08745Sheppo attr.mode = vport->ldc_mode;
11341ae08745Sheppo
11351ae08745Sheppo if ((rv = ldc_init(vport->ldc_id, &attr,
11361ae08745Sheppo &vport->ldc_handle)) != 0) {
11371ae08745Sheppo DWARN("vldc_ioctl_opt_op: ldc_init failed, rv=%d\n", rv);
11381ae08745Sheppo goto error_init;
11391ae08745Sheppo }
11401ae08745Sheppo
11411ae08745Sheppo /* register it */
11421ae08745Sheppo if ((rv = ldc_reg_callback(vport->ldc_handle,
11431ae08745Sheppo i_vldc_cb, (caddr_t)vport)) != 0) {
11441ae08745Sheppo DWARN("vldc_ioctl_opt_op: ldc_reg_callback failed, rv=%d\n",
11451ae08745Sheppo rv);
11461ae08745Sheppo goto error_reg;
11471ae08745Sheppo }
11481ae08745Sheppo
11491ae08745Sheppo /* open the channel */
11501ae08745Sheppo if ((rv = ldc_open(vport->ldc_handle)) != 0) {
11511ae08745Sheppo DWARN("vldc_ioctl_opt_op: ldc_open failed, rv=%d\n", rv);
11521ae08745Sheppo goto error_open;
11531ae08745Sheppo }
11541ae08745Sheppo
11551ae08745Sheppo vport->status = VLDC_PORT_READY;
11561ae08745Sheppo
11571ae08745Sheppo /*
11581ae08745Sheppo * Attempt to bring the channel up, but do not
11591ae08745Sheppo * fail if the other end is not up yet.
11601ae08745Sheppo */
11611ae08745Sheppo rv = ldc_up(vport->ldc_handle);
11621ae08745Sheppo if (rv == ECONNREFUSED) {
11631ae08745Sheppo D1("vldc_ioctl_opt_op: remote endpoint not up yet\n");
11641ae08745Sheppo } else if (rv != 0) {
11651ae08745Sheppo DWARN("vldc_ioctl_opt_op: ldc_up failed, rv=%d\n", rv);
11661ae08745Sheppo goto error_up;
11671ae08745Sheppo }
11681ae08745Sheppo
1169a8ea4edeSnarayan rv = ldc_status(vport->ldc_handle, &vport->ldc_status);
1170a8ea4edeSnarayan if (rv != 0) {
1171a8ea4edeSnarayan DWARN("vldc_ioctl_opt_op: vldc@%d:%d could not get ldc "
1172a8ea4edeSnarayan "status, rv=%d\n", vport->inst, vport->number, rv);
1173a8ea4edeSnarayan goto error_up;
1174a8ea4edeSnarayan }
1175a8ea4edeSnarayan
11761ae08745Sheppo D1("vldc_ioctl_opt_op: ldc %ld initialized successfully\n",
11771ae08745Sheppo vport->ldc_id);
11781ae08745Sheppo
11791ae08745Sheppo return (0);
11801ae08745Sheppo
11811ae08745Sheppo error_up:
11821ae08745Sheppo vport->status = VLDC_PORT_OPEN;
11831ae08745Sheppo (void) ldc_close(vport->ldc_handle);
11841ae08745Sheppo error_open:
11851ae08745Sheppo (void) ldc_unreg_callback(vport->ldc_handle);
11861ae08745Sheppo error_reg:
11871ae08745Sheppo (void) ldc_fini(vport->ldc_handle);
11881ae08745Sheppo error_init:
11891ae08745Sheppo return (rv);
11901ae08745Sheppo }
11911ae08745Sheppo
11921ae08745Sheppo /* ioctl to read cookie */
11931ae08745Sheppo static int
i_vldc_ioctl_read_cookie(vldc_port_t * vport,int vldc_instance,void * arg,int mode)11941ae08745Sheppo i_vldc_ioctl_read_cookie(vldc_port_t *vport, int vldc_instance, void *arg,
11951ae08745Sheppo int mode)
11961ae08745Sheppo {
11971ae08745Sheppo vldc_data_t copy_info;
1198d10e4ef2Snarayan uint64_t len, balance, copy_size;
1199d10e4ef2Snarayan caddr_t src_addr, dst_addr;
12001ae08745Sheppo int rv;
12011ae08745Sheppo
12021ae08745Sheppo if (ddi_copyin(arg, ©_info, sizeof (copy_info), mode) == -1) {
12031ae08745Sheppo return (EFAULT);
12041ae08745Sheppo }
12051ae08745Sheppo
1206d10e4ef2Snarayan len = balance = copy_info.length;
1207d10e4ef2Snarayan src_addr = (caddr_t)copy_info.src_addr;
1208d10e4ef2Snarayan dst_addr = (caddr_t)copy_info.dst_addr;
1209d10e4ef2Snarayan while (balance > 0) {
12101ae08745Sheppo
1211d10e4ef2Snarayan /* get the max amount to the copied */
1212d10e4ef2Snarayan copy_size = MIN(balance, vldc_max_cookie);
12131ae08745Sheppo
12141ae08745Sheppo mutex_enter(&vport->minorp->lock);
12151ae08745Sheppo
1216d10e4ef2Snarayan D2("i_vldc_ioctl_read_cookie: vldc@%d:%d reading from 0x%p "
1217d10e4ef2Snarayan "size 0x%lx to 0x%p\n", vldc_instance, vport->number,
1218d10e4ef2Snarayan dst_addr, copy_size, src_addr);
12191ae08745Sheppo
12201ae08745Sheppo /* read from the HV into the temporary buffer */
12213af08d82Slm66018 rv = ldc_mem_rdwr_cookie(vport->ldc_handle, vport->cookie_buf,
1222d10e4ef2Snarayan ©_size, dst_addr, LDC_COPY_IN);
12231ae08745Sheppo if (rv != 0) {
1224d10e4ef2Snarayan DWARN("i_vldc_ioctl_read_cookie: vldc@%d:%d cannot "
1225d10e4ef2Snarayan "read address 0x%p, rv=%d\n",
1226d10e4ef2Snarayan vldc_instance, vport->number, dst_addr, rv);
12271ae08745Sheppo mutex_exit(&vport->minorp->lock);
12281ae08745Sheppo return (EFAULT);
12291ae08745Sheppo }
12301ae08745Sheppo
12311ae08745Sheppo D2("i_vldc_ioctl_read_cookie: vldc@%d:%d read succeeded\n",
12321ae08745Sheppo vldc_instance, vport->number);
12331ae08745Sheppo
12341ae08745Sheppo mutex_exit(&vport->minorp->lock);
12351ae08745Sheppo
1236d10e4ef2Snarayan /*
1237d10e4ef2Snarayan * copy data from temporary buffer out to the
1238d10e4ef2Snarayan * caller and free buffer
1239d10e4ef2Snarayan */
1240d10e4ef2Snarayan rv = ddi_copyout(vport->cookie_buf, src_addr, copy_size, mode);
12411ae08745Sheppo if (rv != 0) {
12421ae08745Sheppo return (EFAULT);
12431ae08745Sheppo }
12441ae08745Sheppo
1245d10e4ef2Snarayan /* adjust len, source and dest */
1246d10e4ef2Snarayan balance -= copy_size;
1247d10e4ef2Snarayan src_addr += copy_size;
1248d10e4ef2Snarayan dst_addr += copy_size;
1249d10e4ef2Snarayan }
1250d10e4ef2Snarayan
12511ae08745Sheppo /* set the structure to reflect outcome */
12521ae08745Sheppo copy_info.length = len;
12531ae08745Sheppo if (ddi_copyout(©_info, arg, sizeof (copy_info), mode) != 0) {
12541ae08745Sheppo return (EFAULT);
12551ae08745Sheppo }
12561ae08745Sheppo
12571ae08745Sheppo return (0);
12581ae08745Sheppo }
12591ae08745Sheppo
12601ae08745Sheppo /* ioctl to write cookie */
12611ae08745Sheppo static int
i_vldc_ioctl_write_cookie(vldc_port_t * vport,int vldc_instance,void * arg,int mode)12621ae08745Sheppo i_vldc_ioctl_write_cookie(vldc_port_t *vport, int vldc_instance, void *arg,
12631ae08745Sheppo int mode)
12641ae08745Sheppo {
12651ae08745Sheppo vldc_data_t copy_info;
1266d10e4ef2Snarayan uint64_t len, balance, copy_size;
1267d10e4ef2Snarayan caddr_t src_addr, dst_addr;
12681ae08745Sheppo int rv;
12691ae08745Sheppo
1270d10e4ef2Snarayan if (ddi_copyin(arg, ©_info, sizeof (copy_info), mode) != 0) {
12711ae08745Sheppo return (EFAULT);
12721ae08745Sheppo }
12731ae08745Sheppo
12741ae08745Sheppo D2("i_vldc_ioctl_write_cookie: vldc@%d:%d writing 0x%lx size 0x%lx "
12751ae08745Sheppo "to 0x%lx\n", vldc_instance, vport->number, copy_info.src_addr,
12761ae08745Sheppo copy_info.length, copy_info.dst_addr);
12771ae08745Sheppo
1278d10e4ef2Snarayan len = balance = copy_info.length;
1279d10e4ef2Snarayan src_addr = (caddr_t)copy_info.src_addr;
1280d10e4ef2Snarayan dst_addr = (caddr_t)copy_info.dst_addr;
1281d10e4ef2Snarayan while (balance > 0) {
12821ae08745Sheppo
1283d10e4ef2Snarayan /* get the max amount to the copied */
1284d10e4ef2Snarayan copy_size = MIN(balance, vldc_max_cookie);
1285d10e4ef2Snarayan
1286d10e4ef2Snarayan /*
1287d10e4ef2Snarayan * copy into the temporary buffer the data
1288d10e4ef2Snarayan * to be written to the HV
1289d10e4ef2Snarayan */
1290d10e4ef2Snarayan if (ddi_copyin((caddr_t)src_addr, vport->cookie_buf,
1291d10e4ef2Snarayan copy_size, mode) != 0) {
12921ae08745Sheppo return (EFAULT);
12931ae08745Sheppo }
12941ae08745Sheppo
12951ae08745Sheppo mutex_enter(&vport->minorp->lock);
12961ae08745Sheppo
12971ae08745Sheppo /* write the data from the temporary buffer to the HV */
12983af08d82Slm66018 rv = ldc_mem_rdwr_cookie(vport->ldc_handle, vport->cookie_buf,
1299d10e4ef2Snarayan ©_size, dst_addr, LDC_COPY_OUT);
13001ae08745Sheppo if (rv != 0) {
1301d10e4ef2Snarayan DWARN("i_vldc_ioctl_write_cookie: vldc@%d:%d "
1302d10e4ef2Snarayan "failed to write at address 0x%p\n, rv=%d",
1303d10e4ef2Snarayan vldc_instance, vport->number, dst_addr, rv);
13041ae08745Sheppo mutex_exit(&vport->minorp->lock);
13051ae08745Sheppo return (EFAULT);
13061ae08745Sheppo }
13071ae08745Sheppo
13081ae08745Sheppo D2("i_vldc_ioctl_write_cookie: vldc@%d:%d write succeeded\n",
13091ae08745Sheppo vldc_instance, vport->number);
13101ae08745Sheppo
13111ae08745Sheppo mutex_exit(&vport->minorp->lock);
13121ae08745Sheppo
1313d10e4ef2Snarayan /* adjust len, source and dest */
1314d10e4ef2Snarayan balance -= copy_size;
1315d10e4ef2Snarayan src_addr += copy_size;
1316d10e4ef2Snarayan dst_addr += copy_size;
1317d10e4ef2Snarayan }
13181ae08745Sheppo
13191ae08745Sheppo /* set the structure to reflect outcome */
13201ae08745Sheppo copy_info.length = len;
13211ae08745Sheppo if (ddi_copyout(©_info, (caddr_t)arg,
13221ae08745Sheppo sizeof (copy_info), mode) != 0) {
13231ae08745Sheppo return (EFAULT);
13241ae08745Sheppo }
13251ae08745Sheppo
13261ae08745Sheppo return (0);
13271ae08745Sheppo }
13281ae08745Sheppo
13291ae08745Sheppo /* vldc specific ioctl option commands */
13301ae08745Sheppo static int
i_vldc_ioctl_opt_op(vldc_port_t * vport,vldc_t * vldcp,void * arg,int mode)13311ae08745Sheppo i_vldc_ioctl_opt_op(vldc_port_t *vport, vldc_t *vldcp, void *arg, int mode)
13321ae08745Sheppo {
13331ae08745Sheppo vldc_opt_op_t vldc_cmd;
13341ae08745Sheppo uint32_t new_mtu;
13351ae08745Sheppo int rv = 0;
13361ae08745Sheppo
13371ae08745Sheppo if (ddi_copyin(arg, &vldc_cmd, sizeof (vldc_cmd), mode) != 0) {
13381ae08745Sheppo return (EFAULT);
13391ae08745Sheppo }
13401ae08745Sheppo
13411ae08745Sheppo D1("vldc_ioctl_opt_op: op %d\n", vldc_cmd.opt_sel);
13421ae08745Sheppo
13431ae08745Sheppo switch (vldc_cmd.opt_sel) {
13441ae08745Sheppo
13451ae08745Sheppo case VLDC_OPT_MTU_SZ:
13461ae08745Sheppo
13471ae08745Sheppo if (vldc_cmd.op_sel == VLDC_OP_GET) {
13481ae08745Sheppo vldc_cmd.opt_val = vport->mtu;
13491ae08745Sheppo if (ddi_copyout(&vldc_cmd, arg,
13501ae08745Sheppo sizeof (vldc_cmd), mode) == -1) {
13511ae08745Sheppo return (EFAULT);
13521ae08745Sheppo }
13531ae08745Sheppo } else {
13541ae08745Sheppo new_mtu = vldc_cmd.opt_val;
13551ae08745Sheppo
13561ae08745Sheppo if ((new_mtu < LDC_PACKET_SIZE) ||
13571ae08745Sheppo (new_mtu > vldc_max_mtu)) {
13581ae08745Sheppo return (EINVAL);
13591ae08745Sheppo }
13601ae08745Sheppo
13611ae08745Sheppo mutex_enter(&vport->minorp->lock);
13621ae08745Sheppo
13631ae08745Sheppo if ((vport->status != VLDC_PORT_CLOSED) &&
13641ae08745Sheppo (new_mtu != vport->mtu)) {
13651ae08745Sheppo /*
13661ae08745Sheppo * The port has buffers allocated since it is
13671ae08745Sheppo * not closed plus the MTU size has changed.
13681ae08745Sheppo * Reallocate the buffers to the new MTU size.
13691ae08745Sheppo */
13701ae08745Sheppo kmem_free(vport->recv_buf, vport->mtu);
13711ae08745Sheppo vport->recv_buf = kmem_alloc(new_mtu, KM_SLEEP);
13721ae08745Sheppo
13731ae08745Sheppo kmem_free(vport->send_buf, vport->mtu);
13741ae08745Sheppo vport->send_buf = kmem_alloc(new_mtu, KM_SLEEP);
13751ae08745Sheppo
13761ae08745Sheppo vport->mtu = new_mtu;
13771ae08745Sheppo }
13781ae08745Sheppo
13791ae08745Sheppo mutex_exit(&vport->minorp->lock);
13801ae08745Sheppo }
13811ae08745Sheppo
13821ae08745Sheppo break;
13831ae08745Sheppo
13841ae08745Sheppo case VLDC_OPT_STATUS:
13851ae08745Sheppo
13861ae08745Sheppo if (vldc_cmd.op_sel == VLDC_OP_GET) {
13871ae08745Sheppo vldc_cmd.opt_val = vport->status;
13881ae08745Sheppo if (ddi_copyout(&vldc_cmd, arg,
13891ae08745Sheppo sizeof (vldc_cmd), mode) == -1) {
13901ae08745Sheppo return (EFAULT);
13911ae08745Sheppo }
13921ae08745Sheppo } else {
13931ae08745Sheppo return (ENOTSUP);
13941ae08745Sheppo }
13951ae08745Sheppo
13961ae08745Sheppo break;
13971ae08745Sheppo
13981ae08745Sheppo case VLDC_OPT_MODE:
13991ae08745Sheppo
14001ae08745Sheppo if (vldc_cmd.op_sel == VLDC_OP_GET) {
14011ae08745Sheppo vldc_cmd.opt_val = vport->ldc_mode;
14021ae08745Sheppo if (ddi_copyout(&vldc_cmd, arg,
14031ae08745Sheppo sizeof (vldc_cmd), mode) == -1) {
14041ae08745Sheppo return (EFAULT);
14051ae08745Sheppo }
14061ae08745Sheppo } else {
14071ae08745Sheppo mutex_enter(&vport->minorp->lock);
14081ae08745Sheppo rv = vldc_set_ldc_mode(vport, vldcp, vldc_cmd.opt_val);
14091ae08745Sheppo mutex_exit(&vport->minorp->lock);
14101ae08745Sheppo }
14111ae08745Sheppo
14121ae08745Sheppo break;
14131ae08745Sheppo
14141ae08745Sheppo default:
14151ae08745Sheppo
14161ae08745Sheppo D1("vldc_ioctl_opt_op: unsupported op %d\n", vldc_cmd.opt_sel);
14171ae08745Sheppo return (ENOTSUP);
14181ae08745Sheppo }
14191ae08745Sheppo
14201ae08745Sheppo return (rv);
14211ae08745Sheppo }
14221ae08745Sheppo
14231ae08745Sheppo /* cb_ioctl */
14241ae08745Sheppo static int
vldc_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)14251ae08745Sheppo vldc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
14261ae08745Sheppo int *rvalp)
14271ae08745Sheppo {
14281ae08745Sheppo _NOTE(ARGUNUSED(credp, rvalp))
14291ae08745Sheppo
14301ae08745Sheppo int rv = EINVAL;
14311ae08745Sheppo int instance;
14321ae08745Sheppo minor_t minor;
14331ae08745Sheppo uint64_t portno;
14341ae08745Sheppo vldc_t *vldcp;
14351ae08745Sheppo vldc_port_t *vport;
14361ae08745Sheppo vldc_minor_t *vminor;
14371ae08745Sheppo
14381ae08745Sheppo minor = getminor(dev);
14391ae08745Sheppo instance = VLDCINST(minor);
14401ae08745Sheppo vldcp = ddi_get_soft_state(vldc_ssp, instance);
14411ae08745Sheppo if (vldcp == NULL) {
14421ae08745Sheppo return (ENXIO);
14431ae08745Sheppo }
14441ae08745Sheppo
14451ae08745Sheppo vminor = VLDCMINOR(vldcp, minor);
14461ae08745Sheppo mutex_enter(&vminor->lock);
14471ae08745Sheppo portno = vminor->portno;
14481ae08745Sheppo if (portno == VLDC_INVALID_PORTNO) {
14491ae08745Sheppo mutex_exit(&vminor->lock);
14501ae08745Sheppo return (ENOLINK);
14511ae08745Sheppo }
14521ae08745Sheppo vminor->in_use += 1;
14531ae08745Sheppo mutex_exit(&vminor->lock);
14541ae08745Sheppo
14551ae08745Sheppo vport = &(vldcp->port[portno]);
14561ae08745Sheppo
14571ae08745Sheppo D1("vldc_ioctl: vldc@%d:%lu cmd=0x%x\n", instance, portno, cmd);
14581ae08745Sheppo
14591ae08745Sheppo switch (cmd) {
14601ae08745Sheppo
14611ae08745Sheppo case VLDC_IOCTL_OPT_OP:
14621ae08745Sheppo rv = i_vldc_ioctl_opt_op(vport, vldcp, (void *)arg, mode);
14631ae08745Sheppo break;
14641ae08745Sheppo
14651ae08745Sheppo case VLDC_IOCTL_READ_COOKIE:
1466d10e4ef2Snarayan if (strcmp(vport->minorp->sname, VLDC_HVCTL_SVCNAME)) {
1467d10e4ef2Snarayan rv = EINVAL;
1468d10e4ef2Snarayan break;
1469d10e4ef2Snarayan }
14701ae08745Sheppo rv = i_vldc_ioctl_read_cookie(vport, instance,
14711ae08745Sheppo (void *)arg, mode);
14721ae08745Sheppo break;
14731ae08745Sheppo
14741ae08745Sheppo case VLDC_IOCTL_WRITE_COOKIE:
1475d10e4ef2Snarayan if (strcmp(vport->minorp->sname, VLDC_HVCTL_SVCNAME)) {
1476d10e4ef2Snarayan rv = EINVAL;
1477d10e4ef2Snarayan break;
1478d10e4ef2Snarayan }
14791ae08745Sheppo rv = i_vldc_ioctl_write_cookie(vport, instance,
14801ae08745Sheppo (void *)arg, mode);
14811ae08745Sheppo break;
14821ae08745Sheppo
14831ae08745Sheppo default:
14841ae08745Sheppo DWARN("vldc_ioctl: vldc@%d:%lu unknown cmd=0x%x\n",
14851ae08745Sheppo instance, portno, cmd);
14861ae08745Sheppo rv = EINVAL;
14871ae08745Sheppo break;
14881ae08745Sheppo }
14891ae08745Sheppo
14901ae08745Sheppo mutex_enter(&vminor->lock);
14911ae08745Sheppo vminor->in_use -= 1;
14921ae08745Sheppo if (vminor->in_use == 0) {
14931ae08745Sheppo cv_signal(&vminor->cv);
14941ae08745Sheppo }
14951ae08745Sheppo mutex_exit(&vminor->lock);
14961ae08745Sheppo
14971ae08745Sheppo D1("vldc_ioctl: rv=%d\n", rv);
14981ae08745Sheppo
14991ae08745Sheppo return (rv);
15001ae08745Sheppo }
15011ae08745Sheppo
15021ae08745Sheppo /* cb_read */
15031ae08745Sheppo static int
vldc_read(dev_t dev,struct uio * uiop,cred_t * credp)15041ae08745Sheppo vldc_read(dev_t dev, struct uio *uiop, cred_t *credp)
15051ae08745Sheppo {
15061ae08745Sheppo _NOTE(ARGUNUSED(credp))
15071ae08745Sheppo
15081ae08745Sheppo int instance;
15091ae08745Sheppo minor_t minor;
15101ae08745Sheppo size_t size = 0;
15111ae08745Sheppo uint64_t portno;
15121ae08745Sheppo vldc_t *vldcp;
15131ae08745Sheppo vldc_port_t *vport;
15141ae08745Sheppo vldc_minor_t *vminor;
15151ae08745Sheppo int rv = 0;
15161ae08745Sheppo
15171ae08745Sheppo minor = getminor(dev);
15181ae08745Sheppo instance = VLDCINST(minor);
15191ae08745Sheppo vldcp = ddi_get_soft_state(vldc_ssp, instance);
15201ae08745Sheppo if (vldcp == NULL) {
15211ae08745Sheppo return (ENXIO);
15221ae08745Sheppo }
15231ae08745Sheppo
15241ae08745Sheppo vminor = VLDCMINOR(vldcp, minor);
15251ae08745Sheppo mutex_enter(&vminor->lock);
15261ae08745Sheppo portno = vminor->portno;
15271ae08745Sheppo if (portno == VLDC_INVALID_PORTNO) {
15281ae08745Sheppo mutex_exit(&vminor->lock);
15291ae08745Sheppo return (ENOLINK);
15301ae08745Sheppo }
15311ae08745Sheppo
15321ae08745Sheppo D2("vldc_read: vldc@%d:%lu reading data\n", instance, portno);
15331ae08745Sheppo
15341ae08745Sheppo vport = &(vldcp->port[portno]);
15351ae08745Sheppo
15361ae08745Sheppo /* check the port status */
15371ae08745Sheppo if (vport->status != VLDC_PORT_READY) {
15381ae08745Sheppo DWARN("vldc_read: vldc@%d:%lu not in the ready state\n",
15391ae08745Sheppo instance, portno);
15401ae08745Sheppo mutex_exit(&vminor->lock);
15411ae08745Sheppo return (ENOTACTIVE);
15421ae08745Sheppo }
15431ae08745Sheppo
15441ae08745Sheppo /* read data */
15451ae08745Sheppo size = MIN(vport->mtu, uiop->uio_resid);
15461ae08745Sheppo rv = ldc_read(vport->ldc_handle, vport->recv_buf, &size);
15471ae08745Sheppo
15481ae08745Sheppo D2("vldc_read: vldc@%d:%lu ldc_read size=%ld, rv=%d\n",
15491ae08745Sheppo instance, portno, size, rv);
15501ae08745Sheppo
15511ae08745Sheppo if (rv == 0) {
15521ae08745Sheppo if (size != 0) {
15531ae08745Sheppo rv = uiomove(vport->recv_buf, size, UIO_READ, uiop);
15541ae08745Sheppo } else {
15551ae08745Sheppo rv = EWOULDBLOCK;
15561ae08745Sheppo }
15571ae08745Sheppo } else {
15581ae08745Sheppo switch (rv) {
15591ae08745Sheppo case ENOBUFS:
15601ae08745Sheppo break;
15611ae08745Sheppo case ETIMEDOUT:
15621ae08745Sheppo case EWOULDBLOCK:
15631ae08745Sheppo rv = EWOULDBLOCK;
15641ae08745Sheppo break;
15651ae08745Sheppo default:
15661ae08745Sheppo rv = ECONNRESET;
15671ae08745Sheppo break;
15681ae08745Sheppo }
15691ae08745Sheppo }
15701ae08745Sheppo
15711ae08745Sheppo mutex_exit(&vminor->lock);
15721ae08745Sheppo
15731ae08745Sheppo return (rv);
15741ae08745Sheppo }
15751ae08745Sheppo
15761ae08745Sheppo /* cb_write */
15771ae08745Sheppo static int
vldc_write(dev_t dev,struct uio * uiop,cred_t * credp)15781ae08745Sheppo vldc_write(dev_t dev, struct uio *uiop, cred_t *credp)
15791ae08745Sheppo {
15801ae08745Sheppo _NOTE(ARGUNUSED(credp))
15811ae08745Sheppo
15821ae08745Sheppo int instance;
15831ae08745Sheppo minor_t minor;
15841ae08745Sheppo size_t size;
15851ae08745Sheppo size_t orig_size;
15861ae08745Sheppo uint64_t portno;
15871ae08745Sheppo vldc_t *vldcp;
15881ae08745Sheppo vldc_port_t *vport;
15891ae08745Sheppo vldc_minor_t *vminor;
15901ae08745Sheppo int rv = EINVAL;
15911ae08745Sheppo
15921ae08745Sheppo minor = getminor(dev);
15931ae08745Sheppo instance = VLDCINST(minor);
15941ae08745Sheppo vldcp = ddi_get_soft_state(vldc_ssp, instance);
15951ae08745Sheppo if (vldcp == NULL) {
15961ae08745Sheppo return (ENXIO);
15971ae08745Sheppo }
15981ae08745Sheppo
15991ae08745Sheppo vminor = VLDCMINOR(vldcp, minor);
16001ae08745Sheppo mutex_enter(&vminor->lock);
16011ae08745Sheppo portno = vminor->portno;
16021ae08745Sheppo if (portno == VLDC_INVALID_PORTNO) {
16031ae08745Sheppo mutex_exit(&vminor->lock);
16041ae08745Sheppo return (ENOLINK);
16051ae08745Sheppo }
16061ae08745Sheppo
16071ae08745Sheppo vport = &(vldcp->port[portno]);
16081ae08745Sheppo
16091ae08745Sheppo /* check the port status */
16101ae08745Sheppo if (vport->status != VLDC_PORT_READY) {
16111ae08745Sheppo DWARN("vldc_write: vldc@%d:%lu not in the ready state\n",
16121ae08745Sheppo instance, portno);
16131ae08745Sheppo mutex_exit(&vminor->lock);
16141ae08745Sheppo return (ENOTACTIVE);
16151ae08745Sheppo }
16161ae08745Sheppo
16171ae08745Sheppo orig_size = uiop->uio_resid;
16181ae08745Sheppo size = orig_size;
16191ae08745Sheppo
16201ae08745Sheppo if (size > vport->mtu) {
16211ae08745Sheppo if (vport->is_stream) {
16221ae08745Sheppo /* can only send MTU size at a time */
16231ae08745Sheppo size = vport->mtu;
16241ae08745Sheppo } else {
16251ae08745Sheppo mutex_exit(&vminor->lock);
16261ae08745Sheppo return (EMSGSIZE);
16271ae08745Sheppo }
16281ae08745Sheppo }
16291ae08745Sheppo
16301ae08745Sheppo D2("vldc_write: vldc@%d:%lu writing %lu bytes\n", instance, portno,
16311ae08745Sheppo size);
16321ae08745Sheppo
16331ae08745Sheppo rv = uiomove(vport->send_buf, size, UIO_WRITE, uiop);
16341ae08745Sheppo if (rv == 0) {
16351ae08745Sheppo rv = ldc_write(vport->ldc_handle, (caddr_t)vport->send_buf,
16361ae08745Sheppo &size);
16371ae08745Sheppo if (rv != 0) {
16381ae08745Sheppo DWARN("vldc_write: vldc@%d:%lu failed writing %lu "
16391ae08745Sheppo "bytes rv=%d\n", instance, portno, size, rv);
16401ae08745Sheppo }
16411ae08745Sheppo } else {
16421ae08745Sheppo size = 0;
16431ae08745Sheppo }
16441ae08745Sheppo
16451ae08745Sheppo mutex_exit(&vminor->lock);
16461ae08745Sheppo
16471ae08745Sheppo /* resid is total number of bytes *not* sent */
16481ae08745Sheppo uiop->uio_resid = orig_size - size;
16491ae08745Sheppo
16501ae08745Sheppo return (rv);
16511ae08745Sheppo }
16521ae08745Sheppo
16531ae08745Sheppo /* cb_chpoll */
16541ae08745Sheppo static int
vldc_chpoll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)16551ae08745Sheppo vldc_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
16561ae08745Sheppo struct pollhead **phpp)
16571ae08745Sheppo {
16581ae08745Sheppo int instance;
16591ae08745Sheppo minor_t minor;
16601ae08745Sheppo uint64_t portno;
16611ae08745Sheppo vldc_t *vldcp;
16621ae08745Sheppo vldc_port_t *vport;
16631ae08745Sheppo vldc_minor_t *vminor;
1664e1ebb9ecSlm66018 boolean_t haspkts;
16651ae08745Sheppo
16661ae08745Sheppo minor = getminor(dev);
16671ae08745Sheppo instance = VLDCINST(minor);
16681ae08745Sheppo vldcp = ddi_get_soft_state(vldc_ssp, instance);
16691ae08745Sheppo if (vldcp == NULL) {
16701ae08745Sheppo return (ENXIO);
16711ae08745Sheppo }
16721ae08745Sheppo
16731ae08745Sheppo vminor = VLDCMINOR(vldcp, minor);
16741ae08745Sheppo mutex_enter(&vminor->lock);
16751ae08745Sheppo portno = vminor->portno;
16761ae08745Sheppo if (portno == VLDC_INVALID_PORTNO) {
16771ae08745Sheppo mutex_exit(&vminor->lock);
16781ae08745Sheppo return (ENOLINK);
16791ae08745Sheppo }
16801ae08745Sheppo
16811ae08745Sheppo vport = &(vldcp->port[portno]);
16821ae08745Sheppo
16831ae08745Sheppo /* check the port status */
16841ae08745Sheppo if (vport->status != VLDC_PORT_READY) {
16851ae08745Sheppo mutex_exit(&vminor->lock);
16861ae08745Sheppo return (ENOTACTIVE);
16871ae08745Sheppo }
16881ae08745Sheppo
16891ae08745Sheppo D2("vldc_chpoll: vldc@%d:%lu polling events 0x%x\n",
16901ae08745Sheppo instance, portno, events);
16911ae08745Sheppo
16921ae08745Sheppo *reventsp = 0;
16931ae08745Sheppo
1694a8ea4edeSnarayan if (vport->ldc_status == LDC_UP) {
16951ae08745Sheppo /*
16961ae08745Sheppo * Check if the receive queue is empty and if not, signal that
16971ae08745Sheppo * there is data ready to read.
16981ae08745Sheppo */
16991ae08745Sheppo if (events & POLLIN) {
1700e1ebb9ecSlm66018 if ((ldc_chkq(vport->ldc_handle, &haspkts) == 0) &&
1701e1ebb9ecSlm66018 haspkts) {
17021ae08745Sheppo *reventsp |= POLLIN;
17031ae08745Sheppo }
17041ae08745Sheppo }
17051ae08745Sheppo
17061ae08745Sheppo if (events & POLLOUT)
17071ae08745Sheppo *reventsp |= POLLOUT;
17081ae08745Sheppo
17091ae08745Sheppo } else if (vport->hanged_up) {
17101ae08745Sheppo *reventsp |= POLLHUP;
17111ae08745Sheppo vport->hanged_up = B_FALSE;
17121ae08745Sheppo }
17131ae08745Sheppo
17141ae08745Sheppo mutex_exit(&vminor->lock);
17151ae08745Sheppo
17161ae08745Sheppo if (((*reventsp) == 0) && (!anyyet)) {
17171ae08745Sheppo *phpp = &vport->poll;
17181ae08745Sheppo }
17191ae08745Sheppo
17201ae08745Sheppo D2("vldc_chpoll: vldc@%d:%lu ev=0x%x, rev=0x%x\n",
17211ae08745Sheppo instance, portno, events, *reventsp);
17221ae08745Sheppo
17231ae08745Sheppo return (0);
17241ae08745Sheppo }
1725