xref: /onnv-gate/usr/src/uts/sun4v/io/ds_common.c (revision 12231:b249e82f04c8)
17697SMichael.Christensen@Sun.COM /*
27697SMichael.Christensen@Sun.COM  * CDDL HEADER START
37697SMichael.Christensen@Sun.COM  *
47697SMichael.Christensen@Sun.COM  * The contents of this file are subject to the terms of the
57697SMichael.Christensen@Sun.COM  * Common Development and Distribution License (the "License").
67697SMichael.Christensen@Sun.COM  * You may not use this file except in compliance with the License.
77697SMichael.Christensen@Sun.COM  *
87697SMichael.Christensen@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97697SMichael.Christensen@Sun.COM  * or http://www.opensolaris.org/os/licensing.
107697SMichael.Christensen@Sun.COM  * See the License for the specific language governing permissions
117697SMichael.Christensen@Sun.COM  * and limitations under the License.
127697SMichael.Christensen@Sun.COM  *
137697SMichael.Christensen@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
147697SMichael.Christensen@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157697SMichael.Christensen@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
167697SMichael.Christensen@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
177697SMichael.Christensen@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
187697SMichael.Christensen@Sun.COM  *
197697SMichael.Christensen@Sun.COM  * CDDL HEADER END
207697SMichael.Christensen@Sun.COM  */
217697SMichael.Christensen@Sun.COM 
227697SMichael.Christensen@Sun.COM /*
23*12231SMichael.Christensen@Sun.COM  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
247697SMichael.Christensen@Sun.COM  */
257697SMichael.Christensen@Sun.COM 
267697SMichael.Christensen@Sun.COM /*
277697SMichael.Christensen@Sun.COM  * Domain Services Module Common Code.
287697SMichael.Christensen@Sun.COM  *
297697SMichael.Christensen@Sun.COM  * This module is intended to be used by both Solaris and the VBSC
307697SMichael.Christensen@Sun.COM  * module.
317697SMichael.Christensen@Sun.COM  */
327697SMichael.Christensen@Sun.COM 
337697SMichael.Christensen@Sun.COM #include <sys/modctl.h>
347697SMichael.Christensen@Sun.COM #include <sys/ksynch.h>
357697SMichael.Christensen@Sun.COM #include <sys/taskq.h>
367697SMichael.Christensen@Sun.COM #include <sys/disp.h>
377697SMichael.Christensen@Sun.COM #include <sys/cmn_err.h>
387697SMichael.Christensen@Sun.COM #include <sys/note.h>
397697SMichael.Christensen@Sun.COM #include <sys/mach_descrip.h>
407697SMichael.Christensen@Sun.COM #include <sys/mdesc.h>
417697SMichael.Christensen@Sun.COM #include <sys/ldc.h>
427697SMichael.Christensen@Sun.COM #include <sys/ds.h>
437697SMichael.Christensen@Sun.COM #include <sys/ds_impl.h>
447697SMichael.Christensen@Sun.COM 
457697SMichael.Christensen@Sun.COM #ifndef MIN
467697SMichael.Christensen@Sun.COM #define	MIN(a, b)	((a) < (b) ? (a) : (b))
477697SMichael.Christensen@Sun.COM #endif
487697SMichael.Christensen@Sun.COM 
497697SMichael.Christensen@Sun.COM #define	DS_DECODE_BUF_LEN		30
507697SMichael.Christensen@Sun.COM 
517697SMichael.Christensen@Sun.COM /*
527697SMichael.Christensen@Sun.COM  * All DS ports in the system
537697SMichael.Christensen@Sun.COM  *
547697SMichael.Christensen@Sun.COM  * The list of DS ports is read in from the MD when the DS module is
557697SMichael.Christensen@Sun.COM  * initialized and is never modified. This eliminates the need for
567697SMichael.Christensen@Sun.COM  * locking to access the port array itself. Access to the individual
577697SMichael.Christensen@Sun.COM  * ports are synchronized at the port level.
587697SMichael.Christensen@Sun.COM  */
597697SMichael.Christensen@Sun.COM ds_port_t	ds_ports[DS_MAX_PORTS];
607697SMichael.Christensen@Sun.COM ds_portset_t	ds_allports;	/* all DS ports in the system */
619535SMichael.Christensen@Sun.COM ds_portset_t	ds_nullport;	/* allows test against null portset */
627697SMichael.Christensen@Sun.COM 
639603SMichael.Christensen@Sun.COM /* DS SP port id */
649603SMichael.Christensen@Sun.COM uint64_t ds_sp_port_id = DS_PORTID_INVALID;
659603SMichael.Christensen@Sun.COM 
667697SMichael.Christensen@Sun.COM /*
677697SMichael.Christensen@Sun.COM  * Table of registered services
687697SMichael.Christensen@Sun.COM  *
697697SMichael.Christensen@Sun.COM  * Locking: Accesses to the table of services are synchronized using
707697SMichael.Christensen@Sun.COM  *   a mutex lock. The reader lock must be held when looking up service
717697SMichael.Christensen@Sun.COM  *   information in the table. The writer lock must be held when any
727697SMichael.Christensen@Sun.COM  *   service information is being modified.
737697SMichael.Christensen@Sun.COM  */
747697SMichael.Christensen@Sun.COM ds_svcs_t	ds_svcs;
757697SMichael.Christensen@Sun.COM 
767697SMichael.Christensen@Sun.COM /*
777697SMichael.Christensen@Sun.COM  * Flag to prevent callbacks while in the middle of DS teardown.
787697SMichael.Christensen@Sun.COM  */
797697SMichael.Christensen@Sun.COM boolean_t ds_enabled = B_FALSE;	/* enable/disable taskq processing */
807697SMichael.Christensen@Sun.COM 
817697SMichael.Christensen@Sun.COM /*
827697SMichael.Christensen@Sun.COM  * Retry count and delay for LDC reads and writes
837697SMichael.Christensen@Sun.COM  */
847697SMichael.Christensen@Sun.COM #ifndef DS_DEFAULT_RETRIES
857697SMichael.Christensen@Sun.COM #define	DS_DEFAULT_RETRIES	10000	/* number of times to retry */
867697SMichael.Christensen@Sun.COM #endif
877697SMichael.Christensen@Sun.COM #ifndef DS_DEFAULT_DELAY
887697SMichael.Christensen@Sun.COM #define	DS_DEFAULT_DELAY	1000	/* usecs to wait between retries */
897697SMichael.Christensen@Sun.COM #endif
907697SMichael.Christensen@Sun.COM 
917697SMichael.Christensen@Sun.COM static int ds_retries = DS_DEFAULT_RETRIES;
927697SMichael.Christensen@Sun.COM static clock_t ds_delay = DS_DEFAULT_DELAY;
937697SMichael.Christensen@Sun.COM 
947697SMichael.Christensen@Sun.COM /*
957697SMichael.Christensen@Sun.COM  * Supported versions of the DS message protocol
967697SMichael.Christensen@Sun.COM  *
977697SMichael.Christensen@Sun.COM  * The version array must be sorted in order from the highest
987697SMichael.Christensen@Sun.COM  * supported version to the lowest. Support for a particular
997697SMichael.Christensen@Sun.COM  * <major>.<minor> version implies all lower minor versions of
1007697SMichael.Christensen@Sun.COM  * that same major version are supported as well.
1017697SMichael.Christensen@Sun.COM  */
1027697SMichael.Christensen@Sun.COM static ds_ver_t ds_vers[] = { { 1, 0 } };
1037697SMichael.Christensen@Sun.COM 
1047697SMichael.Christensen@Sun.COM #define	DS_NUM_VER	(sizeof (ds_vers) / sizeof (ds_vers[0]))
1057697SMichael.Christensen@Sun.COM 
1067697SMichael.Christensen@Sun.COM 
1077697SMichael.Christensen@Sun.COM /* incoming message handling functions */
1087697SMichael.Christensen@Sun.COM typedef void (*ds_msg_handler_t)(ds_port_t *port, caddr_t buf, size_t len);
1097697SMichael.Christensen@Sun.COM static void ds_handle_init_req(ds_port_t *port, caddr_t buf, size_t len);
1107697SMichael.Christensen@Sun.COM static void ds_handle_init_ack(ds_port_t *port, caddr_t buf, size_t len);
1117697SMichael.Christensen@Sun.COM static void ds_handle_init_nack(ds_port_t *port, caddr_t buf, size_t len);
1127697SMichael.Christensen@Sun.COM static void ds_handle_reg_req(ds_port_t *port, caddr_t buf, size_t len);
1137697SMichael.Christensen@Sun.COM static void ds_handle_reg_ack(ds_port_t *port, caddr_t buf, size_t len);
1147697SMichael.Christensen@Sun.COM static void ds_handle_reg_nack(ds_port_t *port, caddr_t buf, size_t len);
1157697SMichael.Christensen@Sun.COM static void ds_handle_unreg_req(ds_port_t *port, caddr_t buf, size_t len);
1167697SMichael.Christensen@Sun.COM static void ds_handle_unreg_ack(ds_port_t *port, caddr_t buf, size_t len);
1177697SMichael.Christensen@Sun.COM static void ds_handle_unreg_nack(ds_port_t *port, caddr_t buf, size_t len);
1187697SMichael.Christensen@Sun.COM static void ds_handle_data(ds_port_t *port, caddr_t buf, size_t len);
1197697SMichael.Christensen@Sun.COM static void ds_handle_nack(ds_port_t *port, caddr_t buf, size_t len);
1207697SMichael.Christensen@Sun.COM 
1217697SMichael.Christensen@Sun.COM /*
1227697SMichael.Christensen@Sun.COM  * DS Message Handler Dispatch Table
1237697SMichael.Christensen@Sun.COM  *
1247697SMichael.Christensen@Sun.COM  * A table used to dispatch all incoming messages. This table
1257697SMichael.Christensen@Sun.COM  * contains handlers for all the fixed message types, as well as
1267697SMichael.Christensen@Sun.COM  * the the messages defined in the 1.0 version of the DS protocol.
1277697SMichael.Christensen@Sun.COM  * The handlers are indexed based on the DS header msg_type values
1287697SMichael.Christensen@Sun.COM  */
1297697SMichael.Christensen@Sun.COM static const ds_msg_handler_t ds_msg_handlers[] = {
1307697SMichael.Christensen@Sun.COM 	ds_handle_init_req,		/* DS_INIT_REQ */
1317697SMichael.Christensen@Sun.COM 	ds_handle_init_ack,		/* DS_INIT_ACK */
1327697SMichael.Christensen@Sun.COM 	ds_handle_init_nack,		/* DS_INIT_NACK */
1337697SMichael.Christensen@Sun.COM 	ds_handle_reg_req,		/* DS_REG_REQ */
1347697SMichael.Christensen@Sun.COM 	ds_handle_reg_ack,		/* DS_REG_ACK */
1357697SMichael.Christensen@Sun.COM 	ds_handle_reg_nack,		/* DS_REG_NACK */
1367697SMichael.Christensen@Sun.COM 	ds_handle_unreg_req,		/* DS_UNREG */
1377697SMichael.Christensen@Sun.COM 	ds_handle_unreg_ack,		/* DS_UNREG_ACK */
1387697SMichael.Christensen@Sun.COM 	ds_handle_unreg_nack,		/* DS_UNREG_NACK */
1397697SMichael.Christensen@Sun.COM 	ds_handle_data,			/* DS_DATA */
1407697SMichael.Christensen@Sun.COM 	ds_handle_nack			/* DS_NACK */
1417697SMichael.Christensen@Sun.COM };
1427697SMichael.Christensen@Sun.COM 
1437697SMichael.Christensen@Sun.COM 
1447697SMichael.Christensen@Sun.COM 
1457697SMichael.Christensen@Sun.COM /* initialization functions */
1467697SMichael.Christensen@Sun.COM static int ds_ldc_init(ds_port_t *port);
1477697SMichael.Christensen@Sun.COM 
1487697SMichael.Christensen@Sun.COM /* event processing functions */
1497697SMichael.Christensen@Sun.COM static uint_t ds_ldc_cb(uint64_t event, caddr_t arg);
1507697SMichael.Christensen@Sun.COM static int ds_recv_msg(ds_port_t *port, caddr_t msgp, size_t *sizep);
1517697SMichael.Christensen@Sun.COM static void ds_handle_up_event(ds_port_t *port);
1527697SMichael.Christensen@Sun.COM static void ds_handle_down_reset_events(ds_port_t *port);
1537697SMichael.Christensen@Sun.COM static void ds_handle_recv(void *arg);
1547697SMichael.Christensen@Sun.COM static void ds_dispatch_event(void *arg);
1557697SMichael.Christensen@Sun.COM 
1567697SMichael.Christensen@Sun.COM /* message sending functions */
1577697SMichael.Christensen@Sun.COM static int ds_send_msg(ds_port_t *port, caddr_t msg, size_t msglen);
1587697SMichael.Christensen@Sun.COM static int ds_send_reg_req(ds_svc_t *svc, ds_port_t *port);
1597697SMichael.Christensen@Sun.COM static void ds_send_unreg_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl);
1607697SMichael.Christensen@Sun.COM static void ds_send_data_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl);
1617697SMichael.Christensen@Sun.COM 
1627697SMichael.Christensen@Sun.COM /* walker functions */
1637697SMichael.Christensen@Sun.COM static int ds_svc_isfree(ds_svc_t *svc, void *arg);
1647697SMichael.Christensen@Sun.COM static int ds_svc_unregister(ds_svc_t *svc, void *arg);
1657697SMichael.Christensen@Sun.COM static int ds_svc_port_up(ds_svc_t *svc, void *arg);
1667697SMichael.Christensen@Sun.COM 
1677697SMichael.Christensen@Sun.COM /* service utilities */
1687697SMichael.Christensen@Sun.COM static void ds_reset_svc(ds_svc_t *svc, ds_port_t *port);
1697697SMichael.Christensen@Sun.COM static int ds_svc_register_onport(ds_svc_t *svc, ds_port_t *port);
17010042SMichael.Christensen@Sun.COM static int ds_svc_register_onport_walker(ds_svc_t *svc, void *arg);
17110042SMichael.Christensen@Sun.COM static void ds_set_port_ready(ds_port_t *port, uint16_t major, uint16_t minor);
1727697SMichael.Christensen@Sun.COM 
1737697SMichael.Christensen@Sun.COM /* port utilities */
1747697SMichael.Christensen@Sun.COM static void ds_port_reset(ds_port_t *port);
1757697SMichael.Christensen@Sun.COM static ldc_status_t ds_update_ldc_state(ds_port_t *port);
1767697SMichael.Christensen@Sun.COM 
1777697SMichael.Christensen@Sun.COM /* misc utilities */
1787697SMichael.Christensen@Sun.COM static void min_max_versions(int num_versions, ds_ver_t *sup_versionsp,
1797697SMichael.Christensen@Sun.COM     uint16_t *min_major, uint16_t *max_major);
1807697SMichael.Christensen@Sun.COM 
1817697SMichael.Christensen@Sun.COM /* debug */
1827697SMichael.Christensen@Sun.COM static char *decode_ldc_events(uint64_t event, char *buf);
1837697SMichael.Christensen@Sun.COM 
1847697SMichael.Christensen@Sun.COM /* loopback */
1857697SMichael.Christensen@Sun.COM static void ds_loopback_register(ds_svc_hdl_t hdl);
1867697SMichael.Christensen@Sun.COM static void ds_loopback_unregister(ds_svc_hdl_t hdl);
1877697SMichael.Christensen@Sun.COM static void ds_loopback_send(ds_svc_hdl_t hdl, void *buf, size_t buflen);
1888172SMichael.Christensen@Sun.COM static int ds_loopback_set_svc(ds_svc_t *svc, ds_capability_t *cap,
1898172SMichael.Christensen@Sun.COM     ds_svc_hdl_t *lb_hdlp);
1907697SMichael.Christensen@Sun.COM 
1917697SMichael.Christensen@Sun.COM /* client handling */
1927697SMichael.Christensen@Sun.COM static int i_ds_hdl_lookup(char *service, uint_t is_client, ds_svc_hdl_t *hdlp,
1937697SMichael.Christensen@Sun.COM     uint_t maxhdls);
1947697SMichael.Christensen@Sun.COM static ds_svc_t *ds_find_clnt_svc_by_hdl_port(ds_svc_hdl_t hdl,
1957697SMichael.Christensen@Sun.COM     ds_port_t *port);
1967697SMichael.Christensen@Sun.COM static ds_svc_t *ds_find_svc_by_id_port(char *svc_id, int is_client,
1977697SMichael.Christensen@Sun.COM     ds_port_t *port);
1987697SMichael.Christensen@Sun.COM static ds_svc_t *ds_svc_clone(ds_svc_t *svc);
1997697SMichael.Christensen@Sun.COM static void ds_check_for_dup_services(ds_svc_t *svc);
2007697SMichael.Christensen@Sun.COM static void ds_delete_svc_entry(ds_svc_t *svc);
2017697SMichael.Christensen@Sun.COM 
2027697SMichael.Christensen@Sun.COM char *
ds_strdup(char * str)2037697SMichael.Christensen@Sun.COM ds_strdup(char *str)
2047697SMichael.Christensen@Sun.COM {
2057697SMichael.Christensen@Sun.COM 	char *newstr;
2067697SMichael.Christensen@Sun.COM 
2077697SMichael.Christensen@Sun.COM 	newstr = DS_MALLOC(strlen(str) + 1);
2087697SMichael.Christensen@Sun.COM 	(void) strcpy(newstr, str);
2097697SMichael.Christensen@Sun.COM 	return (newstr);
2107697SMichael.Christensen@Sun.COM }
2117697SMichael.Christensen@Sun.COM 
2127697SMichael.Christensen@Sun.COM void
ds_common_init(void)2137697SMichael.Christensen@Sun.COM ds_common_init(void)
2147697SMichael.Christensen@Sun.COM {
2157697SMichael.Christensen@Sun.COM 	/* Validate version table */
2167697SMichael.Christensen@Sun.COM 	ASSERT(ds_vers_isvalid(ds_vers, DS_NUM_VER) == DS_VERS_OK);
2177697SMichael.Christensen@Sun.COM 
2187697SMichael.Christensen@Sun.COM 	/* Initialize services table */
2197697SMichael.Christensen@Sun.COM 	ds_init_svcs_tbl(DS_MAXSVCS_INIT);
2207697SMichael.Christensen@Sun.COM 
2217697SMichael.Christensen@Sun.COM 	/* enable callback processing */
2227697SMichael.Christensen@Sun.COM 	ds_enabled = B_TRUE;
2237697SMichael.Christensen@Sun.COM }
2247697SMichael.Christensen@Sun.COM 
2257697SMichael.Christensen@Sun.COM /* BEGIN LDC SUPPORT FUNCTIONS */
2267697SMichael.Christensen@Sun.COM 
2277697SMichael.Christensen@Sun.COM static char *
decode_ldc_events(uint64_t event,char * buf)2287697SMichael.Christensen@Sun.COM decode_ldc_events(uint64_t event, char *buf)
2297697SMichael.Christensen@Sun.COM {
2307697SMichael.Christensen@Sun.COM 	buf[0] = 0;
2317697SMichael.Christensen@Sun.COM 	if (event & LDC_EVT_DOWN)	(void) strcat(buf, " DOWN");
2327697SMichael.Christensen@Sun.COM 	if (event & LDC_EVT_RESET)	(void) strcat(buf, " RESET");
2337697SMichael.Christensen@Sun.COM 	if (event & LDC_EVT_UP)		(void) strcat(buf, " UP");
2347697SMichael.Christensen@Sun.COM 	if (event & LDC_EVT_READ)	(void) strcat(buf, " READ");
2357697SMichael.Christensen@Sun.COM 	if (event & LDC_EVT_WRITE)	(void) strcat(buf, " WRITE");
2367697SMichael.Christensen@Sun.COM 	return (buf);
2377697SMichael.Christensen@Sun.COM }
2387697SMichael.Christensen@Sun.COM 
2397697SMichael.Christensen@Sun.COM static ldc_status_t
ds_update_ldc_state(ds_port_t * port)2407697SMichael.Christensen@Sun.COM ds_update_ldc_state(ds_port_t *port)
2417697SMichael.Christensen@Sun.COM {
2427697SMichael.Christensen@Sun.COM 	ldc_status_t	ldc_state;
2437697SMichael.Christensen@Sun.COM 	int		rv;
2447697SMichael.Christensen@Sun.COM 	char		ebuf[DS_EBUFSIZE];
2457697SMichael.Christensen@Sun.COM 
2467697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&port->lock));
2477697SMichael.Christensen@Sun.COM 
2487697SMichael.Christensen@Sun.COM 	/*
2497697SMichael.Christensen@Sun.COM 	 * Read status and update ldc state info in port structure.
2507697SMichael.Christensen@Sun.COM 	 */
2517697SMichael.Christensen@Sun.COM 	if ((rv = ldc_status(port->ldc.hdl, &ldc_state)) != 0) {
2527697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_status error: %s" DS_EOL,
2537697SMichael.Christensen@Sun.COM 		    PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
2547697SMichael.Christensen@Sun.COM 		ldc_state = port->ldc.state;
2557697SMichael.Christensen@Sun.COM 	} else {
2567697SMichael.Christensen@Sun.COM 		port->ldc.state = ldc_state;
2577697SMichael.Christensen@Sun.COM 	}
2587697SMichael.Christensen@Sun.COM 
2597697SMichael.Christensen@Sun.COM 	return (ldc_state);
2607697SMichael.Christensen@Sun.COM }
2617697SMichael.Christensen@Sun.COM 
2627697SMichael.Christensen@Sun.COM static void
ds_handle_down_reset_events(ds_port_t * port)2637697SMichael.Christensen@Sun.COM ds_handle_down_reset_events(ds_port_t *port)
2647697SMichael.Christensen@Sun.COM {
2657697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: entered" DS_EOL, PORTID(port),
2667697SMichael.Christensen@Sun.COM 	    __func__);
2677697SMichael.Christensen@Sun.COM 
2687697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
2697697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
2707697SMichael.Christensen@Sun.COM 
2717697SMichael.Christensen@Sun.COM 	ds_sys_drain_events(port);
2727697SMichael.Christensen@Sun.COM 
2737697SMichael.Christensen@Sun.COM 	(void) ds_update_ldc_state(port);
2747697SMichael.Christensen@Sun.COM 
2757697SMichael.Christensen@Sun.COM 	/* reset the port state */
2767697SMichael.Christensen@Sun.COM 	ds_port_reset(port);
2777697SMichael.Christensen@Sun.COM 
2787697SMichael.Christensen@Sun.COM 	/* acknowledge the reset */
2797697SMichael.Christensen@Sun.COM 	(void) ldc_up(port->ldc.hdl);
2807697SMichael.Christensen@Sun.COM 
2817697SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
2827697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
2837697SMichael.Christensen@Sun.COM 
2847697SMichael.Christensen@Sun.COM 	ds_handle_up_event(port);
2857697SMichael.Christensen@Sun.COM 
2867697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: exit" DS_EOL, PORTID(port), __func__);
2877697SMichael.Christensen@Sun.COM }
2887697SMichael.Christensen@Sun.COM 
2897697SMichael.Christensen@Sun.COM static void
ds_handle_up_event(ds_port_t * port)2907697SMichael.Christensen@Sun.COM ds_handle_up_event(ds_port_t *port)
2917697SMichael.Christensen@Sun.COM {
2927697SMichael.Christensen@Sun.COM 	ldc_status_t	ldc_state;
2937697SMichael.Christensen@Sun.COM 
2947697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: entered" DS_EOL, PORTID(port),
2957697SMichael.Christensen@Sun.COM 	    __func__);
2967697SMichael.Christensen@Sun.COM 
2977697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
2987697SMichael.Christensen@Sun.COM 
2997697SMichael.Christensen@Sun.COM 	ldc_state = ds_update_ldc_state(port);
3007697SMichael.Christensen@Sun.COM 
3017697SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
3027697SMichael.Christensen@Sun.COM 
3037697SMichael.Christensen@Sun.COM 	if ((ldc_state == LDC_UP) && IS_DS_PORT(port)) {
3047697SMichael.Christensen@Sun.COM 		/*
3057697SMichael.Christensen@Sun.COM 		 * Initiate the handshake.
3067697SMichael.Christensen@Sun.COM 		 */
3077697SMichael.Christensen@Sun.COM 		ds_send_init_req(port);
3087697SMichael.Christensen@Sun.COM 	}
3097697SMichael.Christensen@Sun.COM 
3107697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: exit" DS_EOL, PORTID(port), __func__);
3117697SMichael.Christensen@Sun.COM }
3127697SMichael.Christensen@Sun.COM 
3137697SMichael.Christensen@Sun.COM static uint_t
ds_ldc_cb(uint64_t event,caddr_t arg)3147697SMichael.Christensen@Sun.COM ds_ldc_cb(uint64_t event, caddr_t arg)
3157697SMichael.Christensen@Sun.COM {
3167697SMichael.Christensen@Sun.COM 	ds_port_t	*port = (ds_port_t *)arg;
3177697SMichael.Christensen@Sun.COM 	char		evstring[DS_DECODE_BUF_LEN];
3187697SMichael.Christensen@Sun.COM 
3197697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: %s event (%llx) received" DS_EOL,
3207697SMichael.Christensen@Sun.COM 	    PORTID(port), __func__, decode_ldc_events(event, evstring),
3217697SMichael.Christensen@Sun.COM 	    (u_longlong_t)event);
3227697SMichael.Christensen@Sun.COM 
3237697SMichael.Christensen@Sun.COM 	if (!ds_enabled) {
3247697SMichael.Christensen@Sun.COM 		DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: callback handling is disabled"
3257697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), __func__);
3267697SMichael.Christensen@Sun.COM 		return (LDC_SUCCESS);
3277697SMichael.Christensen@Sun.COM 	}
3287697SMichael.Christensen@Sun.COM 
3297697SMichael.Christensen@Sun.COM 	if (event & (LDC_EVT_DOWN | LDC_EVT_RESET)) {
3307697SMichael.Christensen@Sun.COM 		ds_handle_down_reset_events(port);
3317697SMichael.Christensen@Sun.COM 		goto done;
3327697SMichael.Christensen@Sun.COM 	}
3337697SMichael.Christensen@Sun.COM 
3347697SMichael.Christensen@Sun.COM 	if (event & LDC_EVT_UP) {
3357697SMichael.Christensen@Sun.COM 		ds_handle_up_event(port);
3367697SMichael.Christensen@Sun.COM 	}
3377697SMichael.Christensen@Sun.COM 
3387697SMichael.Christensen@Sun.COM 	if (event & LDC_EVT_READ) {
3397697SMichael.Christensen@Sun.COM 		if (port->ldc.state != LDC_UP) {
3407697SMichael.Christensen@Sun.COM 			cmn_err(CE_WARN, "ds@%lx: %s: LDC READ event while "
3417697SMichael.Christensen@Sun.COM 			    "port not up" DS_EOL, PORTID(port), __func__);
3427697SMichael.Christensen@Sun.COM 			goto done;
3437697SMichael.Christensen@Sun.COM 		}
3447697SMichael.Christensen@Sun.COM 
3457697SMichael.Christensen@Sun.COM 		if (ds_sys_dispatch_func(ds_handle_recv, port)) {
3467697SMichael.Christensen@Sun.COM 			cmn_err(CE_WARN, "ds@%lx: error initiating LDC READ "
3477697SMichael.Christensen@Sun.COM 			    " event", PORTID(port));
3487697SMichael.Christensen@Sun.COM 		}
3497697SMichael.Christensen@Sun.COM 	}
3507697SMichael.Christensen@Sun.COM 
3517697SMichael.Christensen@Sun.COM 	if (event & LDC_EVT_WRITE) {
3527697SMichael.Christensen@Sun.COM 		DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: LDC WRITE event received, "
3537697SMichael.Christensen@Sun.COM 		    "not supported" DS_EOL, PORTID(port), __func__);
3547697SMichael.Christensen@Sun.COM 	}
3557697SMichael.Christensen@Sun.COM 
3567697SMichael.Christensen@Sun.COM 	if (event & ~(LDC_EVT_UP | LDC_EVT_READ)) {
3577697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: %s: Unexpected LDC event received: "
3587697SMichael.Christensen@Sun.COM 		    "0x%llx" DS_EOL, PORTID(port), __func__,
3597697SMichael.Christensen@Sun.COM 		    (u_longlong_t)event);
3607697SMichael.Christensen@Sun.COM 	}
3617697SMichael.Christensen@Sun.COM done:
3627697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: exit" DS_EOL, PORTID(port), __func__);
3637697SMichael.Christensen@Sun.COM 
3647697SMichael.Christensen@Sun.COM 	return (LDC_SUCCESS);
3657697SMichael.Christensen@Sun.COM }
3667697SMichael.Christensen@Sun.COM 
3677697SMichael.Christensen@Sun.COM static int
ds_ldc_init(ds_port_t * port)3687697SMichael.Christensen@Sun.COM ds_ldc_init(ds_port_t *port)
3697697SMichael.Christensen@Sun.COM {
3707697SMichael.Christensen@Sun.COM 	int		rv;
3717697SMichael.Christensen@Sun.COM 	ldc_attr_t	ldc_attr;
3727697SMichael.Christensen@Sun.COM 	caddr_t		ldc_cb_arg = (caddr_t)port;
3737697SMichael.Christensen@Sun.COM 	char		ebuf[DS_EBUFSIZE];
3747697SMichael.Christensen@Sun.COM 
3757697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&port->lock));
3767697SMichael.Christensen@Sun.COM 
3777697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: ldc_id=%lld" DS_EOL,
3787697SMichael.Christensen@Sun.COM 	    PORTID(port), __func__, (u_longlong_t)port->ldc.id);
3797697SMichael.Christensen@Sun.COM 
3807697SMichael.Christensen@Sun.COM 	ldc_attr.devclass = LDC_DEV_GENERIC;
3817697SMichael.Christensen@Sun.COM 	ldc_attr.instance = 0;
3827697SMichael.Christensen@Sun.COM 	ldc_attr.mode = LDC_MODE_RELIABLE;
3837697SMichael.Christensen@Sun.COM 	ldc_attr.mtu = DS_STREAM_MTU;
3847697SMichael.Christensen@Sun.COM 
3857697SMichael.Christensen@Sun.COM 	if ((rv = ldc_init(port->ldc.id, &ldc_attr, &port->ldc.hdl)) != 0) {
3867697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_id: %lx, ldc_init error: %s"
3877697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), __func__, port->ldc.id,
3887697SMichael.Christensen@Sun.COM 		    ds_errno_to_str(rv, ebuf));
3897697SMichael.Christensen@Sun.COM 		return (rv);
3907697SMichael.Christensen@Sun.COM 	}
3917697SMichael.Christensen@Sun.COM 
3927697SMichael.Christensen@Sun.COM 	rv = ldc_reg_callback(port->ldc.hdl, ds_ldc_cb, ldc_cb_arg);
3937697SMichael.Christensen@Sun.COM 	if (rv != 0) {
3947697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_reg_callback error: %s"
3957697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
3967697SMichael.Christensen@Sun.COM 		return (rv);
3977697SMichael.Christensen@Sun.COM 	}
3987697SMichael.Christensen@Sun.COM 
3997697SMichael.Christensen@Sun.COM 	ds_sys_ldc_init(port);
4007697SMichael.Christensen@Sun.COM 	return (0);
4017697SMichael.Christensen@Sun.COM }
4027697SMichael.Christensen@Sun.COM 
4037697SMichael.Christensen@Sun.COM int
ds_ldc_fini(ds_port_t * port)4047697SMichael.Christensen@Sun.COM ds_ldc_fini(ds_port_t *port)
4057697SMichael.Christensen@Sun.COM {
4067697SMichael.Christensen@Sun.COM 	int	rv;
4077697SMichael.Christensen@Sun.COM 	char	ebuf[DS_EBUFSIZE];
4087697SMichael.Christensen@Sun.COM 
4097697SMichael.Christensen@Sun.COM 	ASSERT(port->state >= DS_PORT_LDC_INIT);
4109916SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&port->lock));
4117697SMichael.Christensen@Sun.COM 
4127697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: ldc_id=%ld" DS_EOL, PORTID(port),
4137697SMichael.Christensen@Sun.COM 	    __func__, port->ldc.id);
4147697SMichael.Christensen@Sun.COM 
4157697SMichael.Christensen@Sun.COM 	if ((rv = ldc_close(port->ldc.hdl)) != 0) {
4167697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_close error: %s" DS_EOL,
4177697SMichael.Christensen@Sun.COM 		    PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
4187697SMichael.Christensen@Sun.COM 		return (rv);
4197697SMichael.Christensen@Sun.COM 	}
4207697SMichael.Christensen@Sun.COM 
4217697SMichael.Christensen@Sun.COM 	if ((rv = ldc_unreg_callback(port->ldc.hdl)) != 0) {
4227697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_unreg_callback error: %s"
4237697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
4247697SMichael.Christensen@Sun.COM 		return (rv);
4257697SMichael.Christensen@Sun.COM 	}
4267697SMichael.Christensen@Sun.COM 
4277697SMichael.Christensen@Sun.COM 	if ((rv = ldc_fini(port->ldc.hdl)) != 0) {
4287697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_fini error: %s" DS_EOL,
4297697SMichael.Christensen@Sun.COM 		    PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
4307697SMichael.Christensen@Sun.COM 		return (rv);
4317697SMichael.Christensen@Sun.COM 	}
4327697SMichael.Christensen@Sun.COM 
4339916SMichael.Christensen@Sun.COM 	port->ldc.id = (uint64_t)-1;
4349916SMichael.Christensen@Sun.COM 	port->ldc.hdl = NULL;
4359916SMichael.Christensen@Sun.COM 	port->ldc.state = 0;
4369916SMichael.Christensen@Sun.COM 
4377697SMichael.Christensen@Sun.COM 	return (rv);
4387697SMichael.Christensen@Sun.COM }
4397697SMichael.Christensen@Sun.COM 
4407697SMichael.Christensen@Sun.COM /*
4417697SMichael.Christensen@Sun.COM  * Attempt to read a specified number of bytes from a particular LDC.
4427697SMichael.Christensen@Sun.COM  * Returns zero for success or the return code from the LDC read on
4437697SMichael.Christensen@Sun.COM  * failure. The actual number of bytes read from the LDC is returned
4447697SMichael.Christensen@Sun.COM  * in the size parameter.
4457697SMichael.Christensen@Sun.COM  */
4467697SMichael.Christensen@Sun.COM static int
ds_recv_msg(ds_port_t * port,caddr_t msgp,size_t * sizep)4477697SMichael.Christensen@Sun.COM ds_recv_msg(ds_port_t *port, caddr_t msgp, size_t *sizep)
4487697SMichael.Christensen@Sun.COM {
4497697SMichael.Christensen@Sun.COM 	int	rv = 0;
4507697SMichael.Christensen@Sun.COM 	size_t	bytes_req = *sizep;
4517697SMichael.Christensen@Sun.COM 	size_t	bytes_left = bytes_req;
4527697SMichael.Christensen@Sun.COM 	size_t	nbytes;
4537697SMichael.Christensen@Sun.COM 	int	retry_count = 0;
4547697SMichael.Christensen@Sun.COM 	char	ebuf[DS_EBUFSIZE];
4557697SMichael.Christensen@Sun.COM 
4567697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&port->rcv_lock));
4577697SMichael.Christensen@Sun.COM 
4587697SMichael.Christensen@Sun.COM 	*sizep = 0;
4597697SMichael.Christensen@Sun.COM 
4607697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: attempting to read %ld bytes" DS_EOL,
4617697SMichael.Christensen@Sun.COM 	    PORTID(port), bytes_req);
4627697SMichael.Christensen@Sun.COM 
4637697SMichael.Christensen@Sun.COM 	while (bytes_left > 0) {
4647697SMichael.Christensen@Sun.COM 
4657697SMichael.Christensen@Sun.COM 		nbytes = bytes_left;
4667697SMichael.Christensen@Sun.COM 
4679916SMichael.Christensen@Sun.COM 		mutex_enter(&port->lock);
4689916SMichael.Christensen@Sun.COM 		if (port->ldc.state == LDC_UP) {
4699916SMichael.Christensen@Sun.COM 			rv = ldc_read(port->ldc.hdl, msgp, &nbytes);
4709916SMichael.Christensen@Sun.COM 		} else
4719916SMichael.Christensen@Sun.COM 			rv = ENXIO;
4729916SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
4739916SMichael.Christensen@Sun.COM 		if (rv != 0) {
4747697SMichael.Christensen@Sun.COM 			if (rv == ECONNRESET) {
4757697SMichael.Christensen@Sun.COM 				break;
4767697SMichael.Christensen@Sun.COM 			} else if (rv != EAGAIN) {
4777697SMichael.Christensen@Sun.COM 				cmn_err(CE_NOTE, "ds@%lx: %s: %s" DS_EOL,
4787697SMichael.Christensen@Sun.COM 				    PORTID(port), __func__,
4797697SMichael.Christensen@Sun.COM 				    ds_errno_to_str(rv, ebuf));
4807697SMichael.Christensen@Sun.COM 				break;
4817697SMichael.Christensen@Sun.COM 			}
4827697SMichael.Christensen@Sun.COM 		} else {
4837697SMichael.Christensen@Sun.COM 			if (nbytes != 0) {
4847697SMichael.Christensen@Sun.COM 				DS_DBG_LDC(CE_NOTE, "ds@%lx: "
4857697SMichael.Christensen@Sun.COM 				    "read %ld bytes, %d retries" DS_EOL,
4867697SMichael.Christensen@Sun.COM 				    PORTID(port), nbytes, retry_count);
4877697SMichael.Christensen@Sun.COM 
4887697SMichael.Christensen@Sun.COM 				*sizep += nbytes;
4897697SMichael.Christensen@Sun.COM 				msgp += nbytes;
4907697SMichael.Christensen@Sun.COM 				bytes_left -= nbytes;
4917697SMichael.Christensen@Sun.COM 
4927697SMichael.Christensen@Sun.COM 				/* reset counter on a successful read */
4937697SMichael.Christensen@Sun.COM 				retry_count = 0;
4947697SMichael.Christensen@Sun.COM 				continue;
4957697SMichael.Christensen@Sun.COM 			}
4967697SMichael.Christensen@Sun.COM 
4977697SMichael.Christensen@Sun.COM 			/*
4987697SMichael.Christensen@Sun.COM 			 * No data was read. Check if this is the
4997697SMichael.Christensen@Sun.COM 			 * first attempt. If so, just return since
5007697SMichael.Christensen@Sun.COM 			 * nothing has been read yet.
5017697SMichael.Christensen@Sun.COM 			 */
5027697SMichael.Christensen@Sun.COM 			if (bytes_left == bytes_req) {
5037697SMichael.Christensen@Sun.COM 				DS_DBG_LDC(CE_NOTE, "ds@%lx: read zero bytes, "
5047697SMichael.Christensen@Sun.COM 				    " no data available" DS_EOL, PORTID(port));
5057697SMichael.Christensen@Sun.COM 				break;
5067697SMichael.Christensen@Sun.COM 			}
5077697SMichael.Christensen@Sun.COM 		}
5087697SMichael.Christensen@Sun.COM 
5097697SMichael.Christensen@Sun.COM 		/*
5107697SMichael.Christensen@Sun.COM 		 * A retry is necessary because the read returned
5117697SMichael.Christensen@Sun.COM 		 * EAGAIN, or a zero length read occurred after
5127697SMichael.Christensen@Sun.COM 		 * reading a partial message.
5137697SMichael.Christensen@Sun.COM 		 */
5147697SMichael.Christensen@Sun.COM 		if (retry_count++ >= ds_retries) {
5157697SMichael.Christensen@Sun.COM 			DS_DBG_LDC(CE_NOTE, "ds@%lx: timed out waiting for "
5167697SMichael.Christensen@Sun.COM 			    "message" DS_EOL, PORTID(port));
5177697SMichael.Christensen@Sun.COM 			break;
5187697SMichael.Christensen@Sun.COM 		}
5197697SMichael.Christensen@Sun.COM 
5207697SMichael.Christensen@Sun.COM 		drv_usecwait(ds_delay);
5217697SMichael.Christensen@Sun.COM 	}
5227697SMichael.Christensen@Sun.COM 
5237697SMichael.Christensen@Sun.COM 	return (rv);
5247697SMichael.Christensen@Sun.COM }
5257697SMichael.Christensen@Sun.COM 
5267697SMichael.Christensen@Sun.COM static void
ds_handle_recv(void * arg)5277697SMichael.Christensen@Sun.COM ds_handle_recv(void *arg)
5287697SMichael.Christensen@Sun.COM {
5297697SMichael.Christensen@Sun.COM 	ds_port_t	*port = (ds_port_t *)arg;
5307697SMichael.Christensen@Sun.COM 	char		*hbuf;
5317697SMichael.Christensen@Sun.COM 	size_t		msglen;
5327697SMichael.Christensen@Sun.COM 	size_t		read_size;
5337697SMichael.Christensen@Sun.COM 	boolean_t	hasdata;
5347697SMichael.Christensen@Sun.COM 	ds_hdr_t	hdr;
5357697SMichael.Christensen@Sun.COM 	uint8_t		*msg;
5367697SMichael.Christensen@Sun.COM 	char		*currp;
5377697SMichael.Christensen@Sun.COM 	int		rv;
5387697SMichael.Christensen@Sun.COM 	ds_event_t	*devent;
5397697SMichael.Christensen@Sun.COM 
5407697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s..." DS_EOL, PORTID(port), __func__);
5417697SMichael.Christensen@Sun.COM 
5427697SMichael.Christensen@Sun.COM 	/*
5437697SMichael.Christensen@Sun.COM 	 * Read messages from the channel until there are none
5447697SMichael.Christensen@Sun.COM 	 * pending. Valid messages are dispatched to be handled
5457697SMichael.Christensen@Sun.COM 	 * by a separate thread while any malformed messages are
5467697SMichael.Christensen@Sun.COM 	 * dropped.
5477697SMichael.Christensen@Sun.COM 	 */
5487697SMichael.Christensen@Sun.COM 
5497697SMichael.Christensen@Sun.COM 	mutex_enter(&port->rcv_lock);
5507697SMichael.Christensen@Sun.COM 
5519916SMichael.Christensen@Sun.COM 	for (;;) {
5529916SMichael.Christensen@Sun.COM 		mutex_enter(&port->lock);
5539916SMichael.Christensen@Sun.COM 		if (port->ldc.state == LDC_UP) {
5549916SMichael.Christensen@Sun.COM 			rv = ldc_chkq(port->ldc.hdl, &hasdata);
5559916SMichael.Christensen@Sun.COM 		} else
5569916SMichael.Christensen@Sun.COM 			rv = ENXIO;
5579916SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
5589916SMichael.Christensen@Sun.COM 		if (rv != 0 || !hasdata)
5599916SMichael.Christensen@Sun.COM 			break;
5607697SMichael.Christensen@Sun.COM 
5617697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "ds@%lx: %s: reading next message" DS_EOL,
5627697SMichael.Christensen@Sun.COM 		    PORTID(port), __func__);
5637697SMichael.Christensen@Sun.COM 
5647697SMichael.Christensen@Sun.COM 		/*
5657697SMichael.Christensen@Sun.COM 		 * Read in the next message.
5667697SMichael.Christensen@Sun.COM 		 */
5677697SMichael.Christensen@Sun.COM 		hbuf = (char *)&hdr;
5687697SMichael.Christensen@Sun.COM 		bzero(hbuf, DS_HDR_SZ);
5697697SMichael.Christensen@Sun.COM 		read_size = DS_HDR_SZ;
5707697SMichael.Christensen@Sun.COM 		currp = hbuf;
5717697SMichael.Christensen@Sun.COM 
5727697SMichael.Christensen@Sun.COM 		/* read in the message header */
5737697SMichael.Christensen@Sun.COM 		if ((rv = ds_recv_msg(port, currp, &read_size)) != 0) {
5747697SMichael.Christensen@Sun.COM 			break;
5757697SMichael.Christensen@Sun.COM 		}
5767697SMichael.Christensen@Sun.COM 
5777697SMichael.Christensen@Sun.COM 		if (read_size < DS_HDR_SZ) {
5787697SMichael.Christensen@Sun.COM 			/*
5797697SMichael.Christensen@Sun.COM 			 * A zero length read is a valid signal that
5807697SMichael.Christensen@Sun.COM 			 * there is no data left on the channel.
5817697SMichael.Christensen@Sun.COM 			 */
5827697SMichael.Christensen@Sun.COM 			if (read_size != 0) {
5837697SMichael.Christensen@Sun.COM 				cmn_err(CE_WARN, "ds@%lx: invalid message "
5847697SMichael.Christensen@Sun.COM 				    "length, received %ld bytes, expected %ld"
5857697SMichael.Christensen@Sun.COM 				    DS_EOL, PORTID(port), read_size, DS_HDR_SZ);
5867697SMichael.Christensen@Sun.COM 			}
5877697SMichael.Christensen@Sun.COM 			continue;
5887697SMichael.Christensen@Sun.COM 		}
5897697SMichael.Christensen@Sun.COM 
5907697SMichael.Christensen@Sun.COM 		/* get payload size and allocate a buffer */
5917697SMichael.Christensen@Sun.COM 		read_size = ((ds_hdr_t *)hbuf)->payload_len;
5927697SMichael.Christensen@Sun.COM 		msglen = DS_HDR_SZ + read_size;
5937697SMichael.Christensen@Sun.COM 		msg = DS_MALLOC(msglen);
5947697SMichael.Christensen@Sun.COM 		if (!msg) {
5957697SMichael.Christensen@Sun.COM 			cmn_err(CE_WARN, "Memory allocation failed attempting "
5967697SMichael.Christensen@Sun.COM 			    " to allocate %d bytes." DS_EOL, (int)msglen);
5977697SMichael.Christensen@Sun.COM 			continue;
5987697SMichael.Christensen@Sun.COM 		}
5997697SMichael.Christensen@Sun.COM 
6007697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "ds@%lx: %s: message payload len %d" DS_EOL,
6017697SMichael.Christensen@Sun.COM 		    PORTID(port), __func__, (int)read_size);
6027697SMichael.Christensen@Sun.COM 
6037697SMichael.Christensen@Sun.COM 		/* move message header into buffer */
6047697SMichael.Christensen@Sun.COM 		(void) memcpy(msg, hbuf, DS_HDR_SZ);
6057697SMichael.Christensen@Sun.COM 		currp = (char *)(msg) + DS_HDR_SZ;
6067697SMichael.Christensen@Sun.COM 
6077697SMichael.Christensen@Sun.COM 		/* read in the message body */
6087697SMichael.Christensen@Sun.COM 		if ((rv = ds_recv_msg(port, currp, &read_size)) != 0) {
6097697SMichael.Christensen@Sun.COM 			DS_FREE(msg, msglen);
6107697SMichael.Christensen@Sun.COM 			break;
6117697SMichael.Christensen@Sun.COM 		}
6127697SMichael.Christensen@Sun.COM 
6137697SMichael.Christensen@Sun.COM 		/* validate the size of the message */
6147697SMichael.Christensen@Sun.COM 		if ((DS_HDR_SZ + read_size) != msglen) {
6157697SMichael.Christensen@Sun.COM 			cmn_err(CE_WARN, "ds@%lx: %s: invalid message length, "
6167697SMichael.Christensen@Sun.COM 			    "received %ld bytes, expected %ld" DS_EOL,
6177697SMichael.Christensen@Sun.COM 			    PORTID(port), __func__, (DS_HDR_SZ + read_size),
6187697SMichael.Christensen@Sun.COM 			    msglen);
6197697SMichael.Christensen@Sun.COM 			DS_FREE(msg, msglen);
6207697SMichael.Christensen@Sun.COM 			continue;
6217697SMichael.Christensen@Sun.COM 		}
6227697SMichael.Christensen@Sun.COM 
6237697SMichael.Christensen@Sun.COM 		DS_DUMP_MSG(DS_DBG_FLAG_LDC, msg, msglen);
6247697SMichael.Christensen@Sun.COM 
6257697SMichael.Christensen@Sun.COM 		/*
6267697SMichael.Christensen@Sun.COM 		 * Send the message for processing, and store it
6277697SMichael.Christensen@Sun.COM 		 * in the log. The memory is deallocated only when
6287697SMichael.Christensen@Sun.COM 		 * the message is removed from the log.
6297697SMichael.Christensen@Sun.COM 		 */
6307697SMichael.Christensen@Sun.COM 
6317697SMichael.Christensen@Sun.COM 		devent = DS_MALLOC(sizeof (ds_event_t));
6327697SMichael.Christensen@Sun.COM 		devent->port = port;
6337697SMichael.Christensen@Sun.COM 		devent->buf = (char *)msg;
6347697SMichael.Christensen@Sun.COM 		devent->buflen = msglen;
6357697SMichael.Christensen@Sun.COM 
6367697SMichael.Christensen@Sun.COM 		/* log the message */
6377697SMichael.Christensen@Sun.COM 		(void) ds_log_add_msg(DS_LOG_IN(port->id), msg, msglen);
6387697SMichael.Christensen@Sun.COM 
6397697SMichael.Christensen@Sun.COM 		if (ds_sys_dispatch_func(ds_dispatch_event, devent)) {
6407697SMichael.Christensen@Sun.COM 			cmn_err(CE_WARN, "ds@%lx: error initiating "
6417697SMichael.Christensen@Sun.COM 			    "event handler", PORTID(port));
6427697SMichael.Christensen@Sun.COM 			DS_FREE(devent, sizeof (ds_event_t));
6437697SMichael.Christensen@Sun.COM 		}
6447697SMichael.Christensen@Sun.COM 	}
6457697SMichael.Christensen@Sun.COM 
6467697SMichael.Christensen@Sun.COM 	mutex_exit(&port->rcv_lock);
6477697SMichael.Christensen@Sun.COM 
6487697SMichael.Christensen@Sun.COM 	/* handle connection reset errors returned from ds_recv_msg */
6497697SMichael.Christensen@Sun.COM 	if (rv == ECONNRESET) {
6507697SMichael.Christensen@Sun.COM 		ds_handle_down_reset_events(port);
6517697SMichael.Christensen@Sun.COM 	}
6527697SMichael.Christensen@Sun.COM 
6537697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s done" DS_EOL, PORTID(port), __func__);
6547697SMichael.Christensen@Sun.COM }
6557697SMichael.Christensen@Sun.COM 
6567697SMichael.Christensen@Sun.COM static void
ds_dispatch_event(void * arg)6577697SMichael.Christensen@Sun.COM ds_dispatch_event(void *arg)
6587697SMichael.Christensen@Sun.COM {
6597697SMichael.Christensen@Sun.COM 	ds_event_t	*event = (ds_event_t *)arg;
6607697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
6617697SMichael.Christensen@Sun.COM 	ds_port_t	*port;
6627697SMichael.Christensen@Sun.COM 
6637697SMichael.Christensen@Sun.COM 	port = event->port;
6647697SMichael.Christensen@Sun.COM 
6657697SMichael.Christensen@Sun.COM 	hdr = (ds_hdr_t *)event->buf;
6667697SMichael.Christensen@Sun.COM 
6677697SMichael.Christensen@Sun.COM 	if (DS_MSG_TYPE_VALID(hdr->msg_type)) {
6687697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "ds@%lx: dispatch_event: msg_type=%d" DS_EOL,
6697697SMichael.Christensen@Sun.COM 		    PORTID(port), hdr->msg_type);
6707697SMichael.Christensen@Sun.COM 
6717697SMichael.Christensen@Sun.COM 		(*ds_msg_handlers[hdr->msg_type])(port, event->buf,
6727697SMichael.Christensen@Sun.COM 		    event->buflen);
6737697SMichael.Christensen@Sun.COM 	} else {
6747697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: dispatch_event: invalid msg "
6757697SMichael.Christensen@Sun.COM 		    "type (%d)" DS_EOL, PORTID(port), hdr->msg_type);
6767697SMichael.Christensen@Sun.COM 	}
6777697SMichael.Christensen@Sun.COM 
6787697SMichael.Christensen@Sun.COM 	DS_FREE(event->buf, event->buflen);
6797697SMichael.Christensen@Sun.COM 	DS_FREE(event, sizeof (ds_event_t));
6807697SMichael.Christensen@Sun.COM }
6817697SMichael.Christensen@Sun.COM 
6827697SMichael.Christensen@Sun.COM int
ds_send_msg(ds_port_t * port,caddr_t msg,size_t msglen)6837697SMichael.Christensen@Sun.COM ds_send_msg(ds_port_t *port, caddr_t msg, size_t msglen)
6847697SMichael.Christensen@Sun.COM {
6857697SMichael.Christensen@Sun.COM 	int	rv;
6867697SMichael.Christensen@Sun.COM 	caddr_t	currp = msg;
6877697SMichael.Christensen@Sun.COM 	size_t	amt_left = msglen;
6887697SMichael.Christensen@Sun.COM 	int	loopcnt = 0;
6897697SMichael.Christensen@Sun.COM 
6907697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s msglen: %ld" DS_EOL, PORTID(port),
6917697SMichael.Christensen@Sun.COM 	    __func__, msglen);
6927697SMichael.Christensen@Sun.COM 	DS_DUMP_MSG(DS_DBG_FLAG_LDC, msg, msglen);
6937697SMichael.Christensen@Sun.COM 
69410042SMichael.Christensen@Sun.COM 	(void) ds_log_add_msg(DS_LOG_OUT(port->id), (uint8_t *)msg, msglen);
69510042SMichael.Christensen@Sun.COM 
6967697SMichael.Christensen@Sun.COM 	/*
6977697SMichael.Christensen@Sun.COM 	 * Ensure that no other messages can be sent on this port by holding
6987697SMichael.Christensen@Sun.COM 	 * the tx_lock mutex in case the write doesn't get sent with one write.
6997697SMichael.Christensen@Sun.COM 	 * This guarantees that the message doesn't become fragmented.
7007697SMichael.Christensen@Sun.COM 	 */
7017697SMichael.Christensen@Sun.COM 	mutex_enter(&port->tx_lock);
7027697SMichael.Christensen@Sun.COM 
7037697SMichael.Christensen@Sun.COM 	do {
7049916SMichael.Christensen@Sun.COM 		mutex_enter(&port->lock);
7059916SMichael.Christensen@Sun.COM 		if (port->ldc.state == LDC_UP) {
7069916SMichael.Christensen@Sun.COM 			rv = ldc_write(port->ldc.hdl, currp, &msglen);
7079916SMichael.Christensen@Sun.COM 		} else
7089916SMichael.Christensen@Sun.COM 			rv = ENXIO;
7099916SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
7109916SMichael.Christensen@Sun.COM 		if (rv != 0) {
7117697SMichael.Christensen@Sun.COM 			if (rv == ECONNRESET) {
7127697SMichael.Christensen@Sun.COM 				mutex_exit(&port->tx_lock);
71310350SMichael.Christensen@Sun.COM 				(void) ds_sys_dispatch_func((void (*)(void *))
71410350SMichael.Christensen@Sun.COM 				    ds_handle_down_reset_events, port);
7157697SMichael.Christensen@Sun.COM 				return (rv);
7167697SMichael.Christensen@Sun.COM 			} else if ((rv == EWOULDBLOCK) &&
7177697SMichael.Christensen@Sun.COM 			    (loopcnt++ < ds_retries)) {
7187697SMichael.Christensen@Sun.COM 				drv_usecwait(ds_delay);
7197697SMichael.Christensen@Sun.COM 			} else {
72010042SMichael.Christensen@Sun.COM 				DS_DBG_PRCL(CE_NOTE, "ds@%lx: send_msg: "
72110042SMichael.Christensen@Sun.COM 				    "ldc_write failed (%d), %d bytes "
72210042SMichael.Christensen@Sun.COM 				    "remaining" DS_EOL, PORTID(port), rv,
72310042SMichael.Christensen@Sun.COM 				    (int)amt_left);
7247697SMichael.Christensen@Sun.COM 				goto error;
7257697SMichael.Christensen@Sun.COM 			}
7267697SMichael.Christensen@Sun.COM 		} else {
7277697SMichael.Christensen@Sun.COM 			amt_left -= msglen;
7287697SMichael.Christensen@Sun.COM 			currp += msglen;
7297697SMichael.Christensen@Sun.COM 			msglen = amt_left;
7307697SMichael.Christensen@Sun.COM 			loopcnt = 0;
7317697SMichael.Christensen@Sun.COM 		}
7327697SMichael.Christensen@Sun.COM 	} while (amt_left > 0);
7337697SMichael.Christensen@Sun.COM error:
7347697SMichael.Christensen@Sun.COM 	mutex_exit(&port->tx_lock);
7357697SMichael.Christensen@Sun.COM 
7367697SMichael.Christensen@Sun.COM 	return (rv);
7377697SMichael.Christensen@Sun.COM }
7387697SMichael.Christensen@Sun.COM 
7397697SMichael.Christensen@Sun.COM /* END LDC SUPPORT FUNCTIONS */
7407697SMichael.Christensen@Sun.COM 
7417697SMichael.Christensen@Sun.COM 
7427697SMichael.Christensen@Sun.COM /* BEGIN DS PROTOCOL SUPPORT FUNCTIONS */
7437697SMichael.Christensen@Sun.COM 
7447697SMichael.Christensen@Sun.COM static void
ds_handle_init_req(ds_port_t * port,caddr_t buf,size_t len)7457697SMichael.Christensen@Sun.COM ds_handle_init_req(ds_port_t *port, caddr_t buf, size_t len)
7467697SMichael.Christensen@Sun.COM {
7477697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
7487697SMichael.Christensen@Sun.COM 	ds_init_ack_t	*ack;
7497697SMichael.Christensen@Sun.COM 	ds_init_nack_t	*nack;
7507697SMichael.Christensen@Sun.COM 	char		*msg;
7517697SMichael.Christensen@Sun.COM 	size_t		msglen;
7527697SMichael.Christensen@Sun.COM 	ds_init_req_t	*req;
7537697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_init_req_t);
7547697SMichael.Christensen@Sun.COM 	uint16_t	new_major;
7557697SMichael.Christensen@Sun.COM 	uint16_t	new_minor;
7567697SMichael.Christensen@Sun.COM 	boolean_t	match;
7577697SMichael.Christensen@Sun.COM 
7587697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
7597697SMichael.Christensen@Sun.COM 	if (len != explen) {
7607697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <init_req: invalid message "
7617697SMichael.Christensen@Sun.COM 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
7627697SMichael.Christensen@Sun.COM 		    explen);
7637697SMichael.Christensen@Sun.COM 		return;
7647697SMichael.Christensen@Sun.COM 	}
7657697SMichael.Christensen@Sun.COM 
7667697SMichael.Christensen@Sun.COM 	req = (ds_init_req_t *)(buf + DS_HDR_SZ);
7677697SMichael.Christensen@Sun.COM 
7687697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_req: ver=%d.%d" DS_EOL,
7697697SMichael.Christensen@Sun.COM 	    PORTID(port), req->major_vers, req->minor_vers);
7707697SMichael.Christensen@Sun.COM 
7717697SMichael.Christensen@Sun.COM 	match = negotiate_version(DS_NUM_VER, &ds_vers[0],
7727697SMichael.Christensen@Sun.COM 	    req->major_vers, &new_major, &new_minor);
7737697SMichael.Christensen@Sun.COM 
7747697SMichael.Christensen@Sun.COM 	/*
7757697SMichael.Christensen@Sun.COM 	 * Check version info. ACK only if the major numbers exactly
7767697SMichael.Christensen@Sun.COM 	 * match. The service entity can retry with a new minor
7777697SMichael.Christensen@Sun.COM 	 * based on the response sent as part of the NACK.
7787697SMichael.Christensen@Sun.COM 	 */
7797697SMichael.Christensen@Sun.COM 	if (match) {
7807697SMichael.Christensen@Sun.COM 		msglen = DS_MSG_LEN(ds_init_ack_t);
7817697SMichael.Christensen@Sun.COM 		msg = DS_MALLOC(msglen);
7827697SMichael.Christensen@Sun.COM 
7837697SMichael.Christensen@Sun.COM 		hdr = (ds_hdr_t *)msg;
7847697SMichael.Christensen@Sun.COM 		hdr->msg_type = DS_INIT_ACK;
7857697SMichael.Christensen@Sun.COM 		hdr->payload_len = sizeof (ds_init_ack_t);
7867697SMichael.Christensen@Sun.COM 
7877697SMichael.Christensen@Sun.COM 		ack = (ds_init_ack_t *)(msg + DS_HDR_SZ);
7887697SMichael.Christensen@Sun.COM 		ack->minor_vers = MIN(new_minor, req->minor_vers);
7897697SMichael.Christensen@Sun.COM 
7907697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_ack>: minor=0x%04X" DS_EOL,
7917697SMichael.Christensen@Sun.COM 		    PORTID(port), MIN(new_minor, req->minor_vers));
7927697SMichael.Christensen@Sun.COM 	} else {
7937697SMichael.Christensen@Sun.COM 		msglen = DS_MSG_LEN(ds_init_nack_t);
7947697SMichael.Christensen@Sun.COM 		msg = DS_MALLOC(msglen);
7957697SMichael.Christensen@Sun.COM 
7967697SMichael.Christensen@Sun.COM 		hdr = (ds_hdr_t *)msg;
7977697SMichael.Christensen@Sun.COM 		hdr->msg_type = DS_INIT_NACK;
7987697SMichael.Christensen@Sun.COM 		hdr->payload_len = sizeof (ds_init_nack_t);
7997697SMichael.Christensen@Sun.COM 
8007697SMichael.Christensen@Sun.COM 		nack = (ds_init_nack_t *)(msg + DS_HDR_SZ);
8017697SMichael.Christensen@Sun.COM 		nack->major_vers = new_major;
8027697SMichael.Christensen@Sun.COM 
8037697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_nack>: major=0x%04X" DS_EOL,
8047697SMichael.Christensen@Sun.COM 		    PORTID(port), new_major);
8057697SMichael.Christensen@Sun.COM 	}
8067697SMichael.Christensen@Sun.COM 
8077697SMichael.Christensen@Sun.COM 	/*
8087697SMichael.Christensen@Sun.COM 	 * Send the response
8097697SMichael.Christensen@Sun.COM 	 */
8107697SMichael.Christensen@Sun.COM 	(void) ds_send_msg(port, msg, msglen);
8117697SMichael.Christensen@Sun.COM 	DS_FREE(msg, msglen);
81210042SMichael.Christensen@Sun.COM 
81310042SMichael.Christensen@Sun.COM 	if (match) {
81410042SMichael.Christensen@Sun.COM 		ds_set_port_ready(port, req->major_vers, ack->minor_vers);
81510042SMichael.Christensen@Sun.COM 	}
8167697SMichael.Christensen@Sun.COM }
8177697SMichael.Christensen@Sun.COM 
8187697SMichael.Christensen@Sun.COM static void
ds_handle_init_ack(ds_port_t * port,caddr_t buf,size_t len)8197697SMichael.Christensen@Sun.COM ds_handle_init_ack(ds_port_t *port, caddr_t buf, size_t len)
8207697SMichael.Christensen@Sun.COM {
8217697SMichael.Christensen@Sun.COM 	ds_init_ack_t	*ack;
8227697SMichael.Christensen@Sun.COM 	ds_ver_t	*ver;
82310042SMichael.Christensen@Sun.COM 	uint16_t	major;
82410042SMichael.Christensen@Sun.COM 	uint16_t	minor;
8257697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_init_ack_t);
8267697SMichael.Christensen@Sun.COM 
8277697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
8287697SMichael.Christensen@Sun.COM 	if (len != explen) {
8297697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <init_ack: invalid message "
8307697SMichael.Christensen@Sun.COM 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
8317697SMichael.Christensen@Sun.COM 		    explen);
8327697SMichael.Christensen@Sun.COM 		return;
8337697SMichael.Christensen@Sun.COM 	}
8347697SMichael.Christensen@Sun.COM 
8357697SMichael.Christensen@Sun.COM 	ack = (ds_init_ack_t *)(buf + DS_HDR_SZ);
8367697SMichael.Christensen@Sun.COM 
8377697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
8387697SMichael.Christensen@Sun.COM 
83910042SMichael.Christensen@Sun.COM 	if (port->state == DS_PORT_READY) {
84010042SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_ack: port ready" DS_EOL,
84110042SMichael.Christensen@Sun.COM 		    PORTID(port));
84210042SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
84310042SMichael.Christensen@Sun.COM 		return;
84410042SMichael.Christensen@Sun.COM 	}
84510042SMichael.Christensen@Sun.COM 
8467697SMichael.Christensen@Sun.COM 	if (port->state != DS_PORT_INIT_REQ) {
8477697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_ack: invalid state: %d"
8487697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), port->state);
8497697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
8507697SMichael.Christensen@Sun.COM 		return;
8517697SMichael.Christensen@Sun.COM 	}
8527697SMichael.Christensen@Sun.COM 
8537697SMichael.Christensen@Sun.COM 	ver = &(ds_vers[port->ver_idx]);
85410042SMichael.Christensen@Sun.COM 	major = ver->major;
85510042SMichael.Christensen@Sun.COM 	minor = MIN(ver->minor, ack->minor_vers);
85610042SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
8577697SMichael.Christensen@Sun.COM 
8587697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_ack: port ready v%d.%d" DS_EOL,
85910042SMichael.Christensen@Sun.COM 	    PORTID(port), major, minor);
86010042SMichael.Christensen@Sun.COM 
86110042SMichael.Christensen@Sun.COM 	ds_set_port_ready(port, major, minor);
8627697SMichael.Christensen@Sun.COM }
8637697SMichael.Christensen@Sun.COM 
8647697SMichael.Christensen@Sun.COM static void
ds_handle_init_nack(ds_port_t * port,caddr_t buf,size_t len)8657697SMichael.Christensen@Sun.COM ds_handle_init_nack(ds_port_t *port, caddr_t buf, size_t len)
8667697SMichael.Christensen@Sun.COM {
8677697SMichael.Christensen@Sun.COM 	int		idx;
8687697SMichael.Christensen@Sun.COM 	ds_init_nack_t	*nack;
8697697SMichael.Christensen@Sun.COM 	ds_ver_t	*ver;
8707697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_init_nack_t);
8717697SMichael.Christensen@Sun.COM 
8727697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
8737697SMichael.Christensen@Sun.COM 	if (len != explen) {
8747697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_WARN, "ds@%lx: <init_nack: invalid message "
8757697SMichael.Christensen@Sun.COM 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
8767697SMichael.Christensen@Sun.COM 		    explen);
8777697SMichael.Christensen@Sun.COM 		return;
8787697SMichael.Christensen@Sun.COM 	}
8797697SMichael.Christensen@Sun.COM 
8807697SMichael.Christensen@Sun.COM 	nack = (ds_init_nack_t *)(buf + DS_HDR_SZ);
8817697SMichael.Christensen@Sun.COM 
8827697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
8837697SMichael.Christensen@Sun.COM 
8847697SMichael.Christensen@Sun.COM 	if (port->state != DS_PORT_INIT_REQ) {
8857697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_nack: invalid state: %d"
8867697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), port->state);
8877697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
8887697SMichael.Christensen@Sun.COM 		return;
8897697SMichael.Christensen@Sun.COM 	}
8907697SMichael.Christensen@Sun.COM 
8917697SMichael.Christensen@Sun.COM 	ver = &(ds_vers[port->ver_idx]);
8927697SMichael.Christensen@Sun.COM 
8937697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_nack: req=v%d.%d, nack=v%d.x"
8947697SMichael.Christensen@Sun.COM 	    DS_EOL, PORTID(port), ver->major, ver->minor, nack->major_vers);
8957697SMichael.Christensen@Sun.COM 
8967697SMichael.Christensen@Sun.COM 	if (nack->major_vers == 0) {
8977697SMichael.Christensen@Sun.COM 		/* no supported protocol version */
8987697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_WARN, "ds@%lx: <init_nack: DS not supported"
8997697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port));
9007697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
9017697SMichael.Christensen@Sun.COM 		return;
9027697SMichael.Christensen@Sun.COM 	}
9037697SMichael.Christensen@Sun.COM 
9047697SMichael.Christensen@Sun.COM 	/*
9057697SMichael.Christensen@Sun.COM 	 * Walk the version list, looking for a major version
9067697SMichael.Christensen@Sun.COM 	 * that is as close to the requested major version as
9077697SMichael.Christensen@Sun.COM 	 * possible.
9087697SMichael.Christensen@Sun.COM 	 */
9097697SMichael.Christensen@Sun.COM 	for (idx = port->ver_idx; idx < DS_NUM_VER; idx++) {
9107697SMichael.Christensen@Sun.COM 		if (ds_vers[idx].major <= nack->major_vers) {
9117697SMichael.Christensen@Sun.COM 			/* found a version to try */
9127697SMichael.Christensen@Sun.COM 			goto done;
9137697SMichael.Christensen@Sun.COM 		}
9147697SMichael.Christensen@Sun.COM 	}
9157697SMichael.Christensen@Sun.COM 
9167697SMichael.Christensen@Sun.COM 	if (idx == DS_NUM_VER) {
9177697SMichael.Christensen@Sun.COM 		/* no supported version */
9187697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_WARN, "ds@%lx: <init_nack: DS v%d.x not "
9197697SMichael.Christensen@Sun.COM 		    "supported" DS_EOL, PORTID(port), nack->major_vers);
9207697SMichael.Christensen@Sun.COM 
9217697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
9227697SMichael.Christensen@Sun.COM 		return;
9237697SMichael.Christensen@Sun.COM 	}
9247697SMichael.Christensen@Sun.COM 
9257697SMichael.Christensen@Sun.COM done:
9267697SMichael.Christensen@Sun.COM 	/* start the handshake again */
9277697SMichael.Christensen@Sun.COM 	port->ver_idx = idx;
9287697SMichael.Christensen@Sun.COM 	port->state = DS_PORT_LDC_INIT;
9297697SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
9307697SMichael.Christensen@Sun.COM 
9317697SMichael.Christensen@Sun.COM 	ds_send_init_req(port);
9327697SMichael.Christensen@Sun.COM 
9337697SMichael.Christensen@Sun.COM }
9347697SMichael.Christensen@Sun.COM 
9357697SMichael.Christensen@Sun.COM static ds_svc_t *
ds_find_svc_by_id_port(char * svc_id,int is_client,ds_port_t * port)9367697SMichael.Christensen@Sun.COM ds_find_svc_by_id_port(char *svc_id, int is_client, ds_port_t *port)
9377697SMichael.Christensen@Sun.COM {
9387697SMichael.Christensen@Sun.COM 	int		idx;
9397697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc, *found_svc = 0;
9407697SMichael.Christensen@Sun.COM 	uint32_t	flag_match = is_client ? DSSF_ISCLIENT : 0;
9417697SMichael.Christensen@Sun.COM 
9427697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
9437697SMichael.Christensen@Sun.COM 
9447697SMichael.Christensen@Sun.COM 	/* walk every table entry */
9457697SMichael.Christensen@Sun.COM 	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
9467697SMichael.Christensen@Sun.COM 		svc = ds_svcs.tbl[idx];
9477697SMichael.Christensen@Sun.COM 		if (DS_SVC_ISFREE(svc))
9487697SMichael.Christensen@Sun.COM 			continue;
9497697SMichael.Christensen@Sun.COM 		if (strcmp(svc->cap.svc_id, svc_id) != 0)
9507697SMichael.Christensen@Sun.COM 			continue;
9517697SMichael.Christensen@Sun.COM 		if ((svc->flags & DSSF_ISCLIENT) != flag_match)
9527697SMichael.Christensen@Sun.COM 			continue;
9537697SMichael.Christensen@Sun.COM 		if (port != NULL && svc->port == port) {
9547697SMichael.Christensen@Sun.COM 			return (svc);
9557697SMichael.Christensen@Sun.COM 		} else if (svc->state == DS_SVC_INACTIVE) {
9567697SMichael.Christensen@Sun.COM 			found_svc = svc;
9577697SMichael.Christensen@Sun.COM 		} else if (!found_svc) {
9587697SMichael.Christensen@Sun.COM 			found_svc = svc;
9597697SMichael.Christensen@Sun.COM 		}
9607697SMichael.Christensen@Sun.COM 	}
9617697SMichael.Christensen@Sun.COM 
9627697SMichael.Christensen@Sun.COM 	return (found_svc);
9637697SMichael.Christensen@Sun.COM }
9647697SMichael.Christensen@Sun.COM 
9657697SMichael.Christensen@Sun.COM static void
ds_handle_reg_req(ds_port_t * port,caddr_t buf,size_t len)9667697SMichael.Christensen@Sun.COM ds_handle_reg_req(ds_port_t *port, caddr_t buf, size_t len)
9677697SMichael.Christensen@Sun.COM {
9687697SMichael.Christensen@Sun.COM 	ds_reg_req_t	*req;
9697697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
9707697SMichael.Christensen@Sun.COM 	ds_reg_ack_t	*ack;
9717697SMichael.Christensen@Sun.COM 	ds_reg_nack_t	*nack;
9727697SMichael.Christensen@Sun.COM 	char		*msg;
9737697SMichael.Christensen@Sun.COM 	size_t		msglen;
9747697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_reg_req_t);
9757697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc = NULL;
9767697SMichael.Christensen@Sun.COM 	ds_ver_t	version;
9777697SMichael.Christensen@Sun.COM 	uint16_t	new_major;
9787697SMichael.Christensen@Sun.COM 	uint16_t	new_minor;
9797697SMichael.Christensen@Sun.COM 	boolean_t	match;
9807697SMichael.Christensen@Sun.COM 
9817697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
9827697SMichael.Christensen@Sun.COM 	if (len < explen) {
9837697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <reg_req: invalid message "
9847697SMichael.Christensen@Sun.COM 		    "length (%ld), expected at least %ld" DS_EOL,
9857697SMichael.Christensen@Sun.COM 		    PORTID(port), len, explen);
9867697SMichael.Christensen@Sun.COM 		return;
9877697SMichael.Christensen@Sun.COM 	}
9887697SMichael.Christensen@Sun.COM 
9897697SMichael.Christensen@Sun.COM 	req = (ds_reg_req_t *)(buf + DS_HDR_SZ);
9907697SMichael.Christensen@Sun.COM 
9917697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' ver=%d.%d, hdl=0x%llx"
9927697SMichael.Christensen@Sun.COM 	    DS_EOL, PORTID(port), req->svc_id, req->major_vers, req->minor_vers,
9937697SMichael.Christensen@Sun.COM 	    (u_longlong_t)req->svc_handle);
9947697SMichael.Christensen@Sun.COM 
9957697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
9967697SMichael.Christensen@Sun.COM 	svc = ds_find_svc_by_id_port(req->svc_id,
9977697SMichael.Christensen@Sun.COM 	    DS_HDL_ISCLIENT(req->svc_handle) == 0, port);
9987697SMichael.Christensen@Sun.COM 	if (svc == NULL) {
9997697SMichael.Christensen@Sun.COM 
10007697SMichael.Christensen@Sun.COM do_reg_nack:
10017697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
10027697SMichael.Christensen@Sun.COM 
10037697SMichael.Christensen@Sun.COM 		msglen = DS_MSG_LEN(ds_reg_nack_t);
10047697SMichael.Christensen@Sun.COM 		msg = DS_MALLOC(msglen);
10057697SMichael.Christensen@Sun.COM 
10067697SMichael.Christensen@Sun.COM 		hdr = (ds_hdr_t *)msg;
10077697SMichael.Christensen@Sun.COM 		hdr->msg_type = DS_REG_NACK;
10087697SMichael.Christensen@Sun.COM 		hdr->payload_len = sizeof (ds_reg_nack_t);
10097697SMichael.Christensen@Sun.COM 
10107697SMichael.Christensen@Sun.COM 		nack = (ds_reg_nack_t *)(msg + DS_HDR_SZ);
10117697SMichael.Christensen@Sun.COM 		nack->svc_handle = req->svc_handle;
10127697SMichael.Christensen@Sun.COM 		nack->result = DS_REG_VER_NACK;
10137697SMichael.Christensen@Sun.COM 		nack->major_vers = 0;
10147697SMichael.Christensen@Sun.COM 
10157697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_nack>: '%s'" DS_EOL,
10167697SMichael.Christensen@Sun.COM 		    PORTID(port), req->svc_id);
10177697SMichael.Christensen@Sun.COM 		/*
10187697SMichael.Christensen@Sun.COM 		 * Send the response
10197697SMichael.Christensen@Sun.COM 		 */
10207697SMichael.Christensen@Sun.COM 		(void) ds_send_msg(port, msg, msglen);
10217697SMichael.Christensen@Sun.COM 		DS_FREE(msg, msglen);
10227697SMichael.Christensen@Sun.COM 		return;
10237697SMichael.Christensen@Sun.COM 	}
10247697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' found, hdl: 0x%llx" DS_EOL,
10257697SMichael.Christensen@Sun.COM 	    PORTID(port), req->svc_id, (u_longlong_t)svc->hdl);
10267697SMichael.Christensen@Sun.COM 
10277697SMichael.Christensen@Sun.COM 	/*
10287697SMichael.Christensen@Sun.COM 	 * A client sends out a reg req in order to force service providers to
10298172SMichael.Christensen@Sun.COM 	 * initiate a reg req from their end (limitation in the protocol).  We
10308172SMichael.Christensen@Sun.COM 	 * expect the service provider to be in the inactive (DS_SVC_INACTIVE)
10318172SMichael.Christensen@Sun.COM 	 * state.  If the service provider has already sent out a reg req (the
10328172SMichael.Christensen@Sun.COM 	 * state is DS_SVC_REG_PENDING) or has already handshaken (the
10338172SMichael.Christensen@Sun.COM 	 * state is DS_SVC_ACTIVE), then we can simply ignore this reg
10348172SMichael.Christensen@Sun.COM 	 * req.  For any other state, we force an unregister before initiating
10358172SMichael.Christensen@Sun.COM 	 * a reg req.
10367697SMichael.Christensen@Sun.COM 	 */
10377697SMichael.Christensen@Sun.COM 
10387697SMichael.Christensen@Sun.COM 	if (DS_HDL_ISCLIENT(req->svc_handle)) {
10398172SMichael.Christensen@Sun.COM 		switch (svc->state) {
10408172SMichael.Christensen@Sun.COM 
10418172SMichael.Christensen@Sun.COM 		case DS_SVC_REG_PENDING:
10428172SMichael.Christensen@Sun.COM 		case DS_SVC_ACTIVE:
10438172SMichael.Christensen@Sun.COM 			DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' pinging "
10448172SMichael.Christensen@Sun.COM 			    "client, state (%x)" DS_EOL, PORTID(port),
10458172SMichael.Christensen@Sun.COM 			    req->svc_id, svc->state);
10468172SMichael.Christensen@Sun.COM 			mutex_exit(&ds_svcs.lock);
10478172SMichael.Christensen@Sun.COM 			return;
10488172SMichael.Christensen@Sun.COM 
10498172SMichael.Christensen@Sun.COM 		case DS_SVC_INACTIVE:
10508172SMichael.Christensen@Sun.COM 			DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' pinging "
10518172SMichael.Christensen@Sun.COM 			    "client" DS_EOL, PORTID(port), req->svc_id);
10528172SMichael.Christensen@Sun.COM 			break;
10538172SMichael.Christensen@Sun.COM 
10548172SMichael.Christensen@Sun.COM 		default:
10558172SMichael.Christensen@Sun.COM 			DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' pinging "
10568172SMichael.Christensen@Sun.COM 			    "client forced unreg, state (%x)" DS_EOL,
10578172SMichael.Christensen@Sun.COM 			    PORTID(port), req->svc_id, svc->state);
10587697SMichael.Christensen@Sun.COM 			(void) ds_svc_unregister(svc, port);
10598172SMichael.Christensen@Sun.COM 			break;
10607697SMichael.Christensen@Sun.COM 		}
10617697SMichael.Christensen@Sun.COM 		(void) ds_svc_port_up(svc, port);
10627697SMichael.Christensen@Sun.COM 		(void) ds_svc_register_onport(svc, port);
10637697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
10647697SMichael.Christensen@Sun.COM 		return;
10657697SMichael.Christensen@Sun.COM 	}
10667697SMichael.Christensen@Sun.COM 
10677697SMichael.Christensen@Sun.COM 	/*
10687697SMichael.Christensen@Sun.COM 	 * Only remote service providers can initiate a registration.  The
10697697SMichael.Christensen@Sun.COM 	 * local sevice from here must be a client service.
10707697SMichael.Christensen@Sun.COM 	 */
10717697SMichael.Christensen@Sun.COM 
10727697SMichael.Christensen@Sun.COM 	match = negotiate_version(svc->cap.nvers, svc->cap.vers,
10737697SMichael.Christensen@Sun.COM 	    req->major_vers, &new_major, &new_minor);
10747697SMichael.Christensen@Sun.COM 
10757697SMichael.Christensen@Sun.COM 	/*
10767697SMichael.Christensen@Sun.COM 	 * Check version info. ACK only if the major numbers exactly
10777697SMichael.Christensen@Sun.COM 	 * match. The service entity can retry with a new minor
10787697SMichael.Christensen@Sun.COM 	 * based on the response sent as part of the NACK.
10797697SMichael.Christensen@Sun.COM 	 */
10807697SMichael.Christensen@Sun.COM 	if (match) {
10817697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' svc%d: state: %x "
10827697SMichael.Christensen@Sun.COM 		    "svc_portid: %d" DS_EOL, PORTID(port), req->svc_id,
10837697SMichael.Christensen@Sun.COM 		    (int)DS_HDL2IDX(svc->hdl), svc->state,
10847697SMichael.Christensen@Sun.COM 		    (int)(svc->port == NULL ? -1 : PORTID(svc->port)));
10857697SMichael.Christensen@Sun.COM 		/*
10867697SMichael.Christensen@Sun.COM 		 * If the current local service is already in use and
10877697SMichael.Christensen@Sun.COM 		 * it's not on this port, clone it.
10887697SMichael.Christensen@Sun.COM 		 */
10897697SMichael.Christensen@Sun.COM 		if (svc->state != DS_SVC_INACTIVE) {
10907697SMichael.Christensen@Sun.COM 			if (svc->port != NULL && port == svc->port) {
10917697SMichael.Christensen@Sun.COM 				/*
10927697SMichael.Christensen@Sun.COM 				 * Someone probably dropped an unreg req
10937697SMichael.Christensen@Sun.COM 				 * somewhere.  Force a local unreg.
10947697SMichael.Christensen@Sun.COM 				 */
10957697SMichael.Christensen@Sun.COM 				(void) ds_svc_unregister(svc, port);
10967697SMichael.Christensen@Sun.COM 			} else if (!DS_HDL_ISCLIENT(svc->hdl)) {
10977697SMichael.Christensen@Sun.COM 				/*
10987697SMichael.Christensen@Sun.COM 				 * Can't clone a non-client (service provider)
10997697SMichael.Christensen@Sun.COM 				 * handle.  This is because old in-kernel
11007697SMichael.Christensen@Sun.COM 				 * service providers can't deal with multiple
11017697SMichael.Christensen@Sun.COM 				 * handles.
11027697SMichael.Christensen@Sun.COM 				 */
11037697SMichael.Christensen@Sun.COM 				goto do_reg_nack;
11047697SMichael.Christensen@Sun.COM 			} else {
11057697SMichael.Christensen@Sun.COM 				svc = ds_svc_clone(svc);
11067697SMichael.Christensen@Sun.COM 			}
11077697SMichael.Christensen@Sun.COM 		}
11087697SMichael.Christensen@Sun.COM 		svc->port = port;
11097697SMichael.Christensen@Sun.COM 		svc->svc_hdl = req->svc_handle;
11107697SMichael.Christensen@Sun.COM 		svc->state = DS_SVC_ACTIVE;
11117697SMichael.Christensen@Sun.COM 
11127697SMichael.Christensen@Sun.COM 		msglen = DS_MSG_LEN(ds_reg_ack_t);
11137697SMichael.Christensen@Sun.COM 		msg = DS_MALLOC(msglen);
11147697SMichael.Christensen@Sun.COM 
11157697SMichael.Christensen@Sun.COM 		hdr = (ds_hdr_t *)msg;
11167697SMichael.Christensen@Sun.COM 		hdr->msg_type = DS_REG_ACK;
11177697SMichael.Christensen@Sun.COM 		hdr->payload_len = sizeof (ds_reg_ack_t);
11187697SMichael.Christensen@Sun.COM 
11197697SMichael.Christensen@Sun.COM 		ack = (ds_reg_ack_t *)(msg + DS_HDR_SZ);
11207697SMichael.Christensen@Sun.COM 		ack->svc_handle = req->svc_handle;
11217697SMichael.Christensen@Sun.COM 		ack->minor_vers = MIN(new_minor, req->minor_vers);
11227697SMichael.Christensen@Sun.COM 
11237697SMichael.Christensen@Sun.COM 
11247697SMichael.Christensen@Sun.COM 		if (svc->ops.ds_reg_cb) {
11257697SMichael.Christensen@Sun.COM 			/* Call the registration callback */
11267697SMichael.Christensen@Sun.COM 			version.major = req->major_vers;
11277697SMichael.Christensen@Sun.COM 			version.minor = ack->minor_vers;
11287697SMichael.Christensen@Sun.COM 			(*svc->ops.ds_reg_cb)(svc->ops.cb_arg, &version,
11297697SMichael.Christensen@Sun.COM 			    svc->hdl);
11307697SMichael.Christensen@Sun.COM 		}
11317697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
11327697SMichael.Christensen@Sun.COM 
11337697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_ack>: '%s' minor=0x%04X"
11347697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), svc->cap.svc_id,
11357697SMichael.Christensen@Sun.COM 		    MIN(new_minor, req->minor_vers));
11367697SMichael.Christensen@Sun.COM 	} else {
11377697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
11387697SMichael.Christensen@Sun.COM 
11397697SMichael.Christensen@Sun.COM 		msglen = DS_MSG_LEN(ds_reg_nack_t);
11407697SMichael.Christensen@Sun.COM 		msg = DS_MALLOC(msglen);
11417697SMichael.Christensen@Sun.COM 
11427697SMichael.Christensen@Sun.COM 		hdr = (ds_hdr_t *)msg;
11437697SMichael.Christensen@Sun.COM 		hdr->msg_type = DS_REG_NACK;
11447697SMichael.Christensen@Sun.COM 		hdr->payload_len = sizeof (ds_reg_nack_t);
11457697SMichael.Christensen@Sun.COM 
11467697SMichael.Christensen@Sun.COM 		nack = (ds_reg_nack_t *)(msg + DS_HDR_SZ);
11477697SMichael.Christensen@Sun.COM 		nack->svc_handle = req->svc_handle;
11487697SMichael.Christensen@Sun.COM 		nack->result = DS_REG_VER_NACK;
11497697SMichael.Christensen@Sun.COM 		nack->major_vers = new_major;
11507697SMichael.Christensen@Sun.COM 
11517697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_nack>: '%s' major=0x%04X"
11527697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), svc->cap.svc_id, new_major);
11537697SMichael.Christensen@Sun.COM 	}
11547697SMichael.Christensen@Sun.COM 
11557697SMichael.Christensen@Sun.COM 	/* send message */
11567697SMichael.Christensen@Sun.COM 	(void) ds_send_msg(port, msg, msglen);
11577697SMichael.Christensen@Sun.COM 	DS_FREE(msg, msglen);
11587697SMichael.Christensen@Sun.COM }
11597697SMichael.Christensen@Sun.COM 
11607697SMichael.Christensen@Sun.COM static void
ds_handle_reg_ack(ds_port_t * port,caddr_t buf,size_t len)11617697SMichael.Christensen@Sun.COM ds_handle_reg_ack(ds_port_t *port, caddr_t buf, size_t len)
11627697SMichael.Christensen@Sun.COM {
11637697SMichael.Christensen@Sun.COM 	ds_reg_ack_t	*ack;
11647697SMichael.Christensen@Sun.COM 	ds_ver_t	*ver;
11657697SMichael.Christensen@Sun.COM 	ds_ver_t	tmpver;
11667697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
11677697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_reg_ack_t);
11687697SMichael.Christensen@Sun.COM 
11697697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
11707697SMichael.Christensen@Sun.COM 	if (len != explen) {
11717697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <reg_ack: invalid message "
11727697SMichael.Christensen@Sun.COM 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
11737697SMichael.Christensen@Sun.COM 		    explen);
11747697SMichael.Christensen@Sun.COM 		return;
11757697SMichael.Christensen@Sun.COM 	}
11767697SMichael.Christensen@Sun.COM 
11777697SMichael.Christensen@Sun.COM 	ack = (ds_reg_ack_t *)(buf + DS_HDR_SZ);
11787697SMichael.Christensen@Sun.COM 
11797697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
11807697SMichael.Christensen@Sun.COM 
11817697SMichael.Christensen@Sun.COM 	/*
11827697SMichael.Christensen@Sun.COM 	 * This searches for service based on how we generate handles
11837697SMichael.Christensen@Sun.COM 	 * and so only works because this is a reg ack.
11847697SMichael.Christensen@Sun.COM 	 */
11857697SMichael.Christensen@Sun.COM 	if (DS_HDL_ISCLIENT(ack->svc_handle) ||
11867697SMichael.Christensen@Sun.COM 	    (svc = ds_get_svc(ack->svc_handle)) == NULL) {
11877697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <reg_ack: invalid handle 0x%llx"
11887697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), (u_longlong_t)ack->svc_handle);
11897697SMichael.Christensen@Sun.COM 		goto done;
11907697SMichael.Christensen@Sun.COM 	}
11917697SMichael.Christensen@Sun.COM 
11927697SMichael.Christensen@Sun.COM 	/* make sure the message makes sense */
11937697SMichael.Christensen@Sun.COM 	if (svc->state != DS_SVC_REG_PENDING) {
11947697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <reg_ack: invalid state (%d)" DS_EOL,
11957697SMichael.Christensen@Sun.COM 		    PORTID(port), svc->state);
11967697SMichael.Christensen@Sun.COM 		goto done;
11977697SMichael.Christensen@Sun.COM 	}
11987697SMichael.Christensen@Sun.COM 
11997697SMichael.Christensen@Sun.COM 	ver = &(svc->cap.vers[svc->ver_idx]);
12007697SMichael.Christensen@Sun.COM 
12017697SMichael.Christensen@Sun.COM 	/* major version has been agreed upon */
12027697SMichael.Christensen@Sun.COM 	svc->ver.major = ver->major;
12037697SMichael.Christensen@Sun.COM 
12047697SMichael.Christensen@Sun.COM 	if (ack->minor_vers >= ver->minor) {
12057697SMichael.Christensen@Sun.COM 		/*
12067697SMichael.Christensen@Sun.COM 		 * Use the minor version specified in the
12077697SMichael.Christensen@Sun.COM 		 * original request.
12087697SMichael.Christensen@Sun.COM 		 */
12097697SMichael.Christensen@Sun.COM 		svc->ver.minor = ver->minor;
12107697SMichael.Christensen@Sun.COM 	} else {
12117697SMichael.Christensen@Sun.COM 		/*
12127697SMichael.Christensen@Sun.COM 		 * Use the lower minor version returned in
12137697SMichael.Christensen@Sun.COM 		 * the ack. By defninition, all lower minor
12147697SMichael.Christensen@Sun.COM 		 * versions must be supported.
12157697SMichael.Christensen@Sun.COM 		 */
12167697SMichael.Christensen@Sun.COM 		svc->ver.minor = ack->minor_vers;
12177697SMichael.Christensen@Sun.COM 	}
12187697SMichael.Christensen@Sun.COM 
12197697SMichael.Christensen@Sun.COM 	svc->state = DS_SVC_ACTIVE;
12207697SMichael.Christensen@Sun.COM 	svc->port = port;
12217697SMichael.Christensen@Sun.COM 
12227697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_ack: '%s' v%d.%d ready, hdl=0x%llx"
12237697SMichael.Christensen@Sun.COM 	    DS_EOL, PORTID(port), svc->cap.svc_id, svc->ver.major,
12247697SMichael.Christensen@Sun.COM 	    svc->ver.minor, (u_longlong_t)svc->hdl);
12257697SMichael.Christensen@Sun.COM 
12267697SMichael.Christensen@Sun.COM 	/* notify the client that registration is complete */
12277697SMichael.Christensen@Sun.COM 	if (svc->ops.ds_reg_cb) {
12287697SMichael.Christensen@Sun.COM 		/*
12297697SMichael.Christensen@Sun.COM 		 * Use a temporary version structure so that
12307697SMichael.Christensen@Sun.COM 		 * the copy in the svc structure cannot be
12317697SMichael.Christensen@Sun.COM 		 * modified by the client.
12327697SMichael.Christensen@Sun.COM 		 */
12337697SMichael.Christensen@Sun.COM 		tmpver.major = svc->ver.major;
12347697SMichael.Christensen@Sun.COM 		tmpver.minor = svc->ver.minor;
12357697SMichael.Christensen@Sun.COM 
12367697SMichael.Christensen@Sun.COM 		(*svc->ops.ds_reg_cb)(svc->ops.cb_arg, &tmpver, svc->hdl);
12377697SMichael.Christensen@Sun.COM 	}
12387697SMichael.Christensen@Sun.COM 
12397697SMichael.Christensen@Sun.COM done:
12407697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
12417697SMichael.Christensen@Sun.COM }
12427697SMichael.Christensen@Sun.COM 
1243*12231SMichael.Christensen@Sun.COM static boolean_t
ds_port_is_ready(ds_port_t * port)1244*12231SMichael.Christensen@Sun.COM ds_port_is_ready(ds_port_t *port)
1245*12231SMichael.Christensen@Sun.COM {
1246*12231SMichael.Christensen@Sun.COM 	boolean_t is_ready;
1247*12231SMichael.Christensen@Sun.COM 
1248*12231SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
1249*12231SMichael.Christensen@Sun.COM 	is_ready = (port->ldc.state == LDC_UP) &&
1250*12231SMichael.Christensen@Sun.COM 	    (port->state == DS_PORT_READY);
1251*12231SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
1252*12231SMichael.Christensen@Sun.COM 	return (is_ready);
1253*12231SMichael.Christensen@Sun.COM }
1254*12231SMichael.Christensen@Sun.COM 
12557697SMichael.Christensen@Sun.COM static void
ds_try_next_port(ds_svc_t * svc,int portid)12567697SMichael.Christensen@Sun.COM ds_try_next_port(ds_svc_t *svc, int portid)
12577697SMichael.Christensen@Sun.COM {
12587697SMichael.Christensen@Sun.COM 	ds_port_t *port;
12597697SMichael.Christensen@Sun.COM 	ds_portset_t totry;
12607697SMichael.Christensen@Sun.COM 	int i;
12617697SMichael.Christensen@Sun.COM 
12627697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%x %s" DS_EOL, portid, __func__);
12639535SMichael.Christensen@Sun.COM 
12649535SMichael.Christensen@Sun.COM 	/*
12659535SMichael.Christensen@Sun.COM 	 * Get the ports that haven't been tried yet and are available to try.
12669535SMichael.Christensen@Sun.COM 	 */
126710042SMichael.Christensen@Sun.COM 	DS_PORTSET_DUP(totry, svc->avail);
12689535SMichael.Christensen@Sun.COM 	for (i = 0; i < DS_MAX_PORTS; i++) {
126910042SMichael.Christensen@Sun.COM 		if (DS_PORT_IN_SET(svc->tried, i))
127010042SMichael.Christensen@Sun.COM 			DS_PORTSET_DEL(totry, i);
12719535SMichael.Christensen@Sun.COM 	}
12729535SMichael.Christensen@Sun.COM 
12737697SMichael.Christensen@Sun.COM 	if (DS_PORTSET_ISNULL(totry))
12747697SMichael.Christensen@Sun.COM 		return;
12757697SMichael.Christensen@Sun.COM 
12767697SMichael.Christensen@Sun.COM 	for (i = 0; i < DS_MAX_PORTS; i++, portid++) {
12777697SMichael.Christensen@Sun.COM 		if (portid >= DS_MAX_PORTS) {
12787697SMichael.Christensen@Sun.COM 			portid = 0;
12797697SMichael.Christensen@Sun.COM 		}
12807697SMichael.Christensen@Sun.COM 
12817697SMichael.Christensen@Sun.COM 		/*
12827697SMichael.Christensen@Sun.COM 		 * If the port is not in the available list,
12837697SMichael.Christensen@Sun.COM 		 * it is not a candidate for registration.
12847697SMichael.Christensen@Sun.COM 		 */
12857697SMichael.Christensen@Sun.COM 		if (!DS_PORT_IN_SET(totry, portid)) {
12867697SMichael.Christensen@Sun.COM 			continue;
12877697SMichael.Christensen@Sun.COM 		}
12887697SMichael.Christensen@Sun.COM 
12897697SMichael.Christensen@Sun.COM 		port = &ds_ports[portid];
1290*12231SMichael.Christensen@Sun.COM 
1291*12231SMichael.Christensen@Sun.COM 		if (!ds_port_is_ready(port))
1292*12231SMichael.Christensen@Sun.COM 			continue;
1293*12231SMichael.Christensen@Sun.COM 
12947697SMichael.Christensen@Sun.COM 		DS_DBG_LDC(CE_NOTE, "ds@%x: %s trying ldc.id: %d" DS_EOL,
12957697SMichael.Christensen@Sun.COM 		    portid, __func__, (uint_t)(port->ldc.id));
1296*12231SMichael.Christensen@Sun.COM 
1297*12231SMichael.Christensen@Sun.COM 		DS_PORTSET_ADD(svc->tried, portid);
1298*12231SMichael.Christensen@Sun.COM 
12997697SMichael.Christensen@Sun.COM 		if (ds_send_reg_req(svc, port) == 0) {
13007697SMichael.Christensen@Sun.COM 			DS_DBG_LDC(CE_NOTE, "ds@%x: %s reg msg send OK" DS_EOL,
13017697SMichael.Christensen@Sun.COM 			    portid, __func__);
13027697SMichael.Christensen@Sun.COM 			/* register sent successfully */
13037697SMichael.Christensen@Sun.COM 			break;
13047697SMichael.Christensen@Sun.COM 		}
13057697SMichael.Christensen@Sun.COM 		DS_DBG_LDC(CE_NOTE, "ds@%x: %s reg msg send FAIL" DS_EOL,
13067697SMichael.Christensen@Sun.COM 		    portid, __func__);
13077697SMichael.Christensen@Sun.COM 
13087697SMichael.Christensen@Sun.COM 		/* reset the service to try the next port */
13097697SMichael.Christensen@Sun.COM 		ds_reset_svc(svc, port);
13107697SMichael.Christensen@Sun.COM 	}
13117697SMichael.Christensen@Sun.COM }
13127697SMichael.Christensen@Sun.COM 
13137697SMichael.Christensen@Sun.COM static void
ds_handle_reg_nack(ds_port_t * port,caddr_t buf,size_t len)13147697SMichael.Christensen@Sun.COM ds_handle_reg_nack(ds_port_t *port, caddr_t buf, size_t len)
13157697SMichael.Christensen@Sun.COM {
13167697SMichael.Christensen@Sun.COM 	ds_reg_nack_t	*nack;
13177697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
13187697SMichael.Christensen@Sun.COM 	int		idx;
13197697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_reg_nack_t);
13207697SMichael.Christensen@Sun.COM 
13217697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
13227697SMichael.Christensen@Sun.COM 	if (len != explen) {
13237697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <reg_nack: invalid message "
13247697SMichael.Christensen@Sun.COM 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
13257697SMichael.Christensen@Sun.COM 		    explen);
13267697SMichael.Christensen@Sun.COM 		return;
13277697SMichael.Christensen@Sun.COM 	}
13287697SMichael.Christensen@Sun.COM 
13297697SMichael.Christensen@Sun.COM 	nack = (ds_reg_nack_t *)(buf + DS_HDR_SZ);
13307697SMichael.Christensen@Sun.COM 
13317697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
13327697SMichael.Christensen@Sun.COM 
13337697SMichael.Christensen@Sun.COM 	/*
13347697SMichael.Christensen@Sun.COM 	 * We expect a reg_nack for a client ping.
13357697SMichael.Christensen@Sun.COM 	 */
13367697SMichael.Christensen@Sun.COM 	if (DS_HDL_ISCLIENT(nack->svc_handle)) {
13377697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: ping hdl: 0x%llx"
13387697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle);
13397697SMichael.Christensen@Sun.COM 		goto done;
13407697SMichael.Christensen@Sun.COM 	}
13417697SMichael.Christensen@Sun.COM 
13427697SMichael.Christensen@Sun.COM 	/*
13437697SMichael.Christensen@Sun.COM 	 * This searches for service based on how we generate handles
13447697SMichael.Christensen@Sun.COM 	 * and so only works because this is a reg nack.
13457697SMichael.Christensen@Sun.COM 	 */
13467697SMichael.Christensen@Sun.COM 	if ((svc = ds_get_svc(nack->svc_handle)) == NULL) {
13477697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <reg_nack: invalid handle 0x%llx"
13487697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle);
13497697SMichael.Christensen@Sun.COM 		goto done;
13507697SMichael.Christensen@Sun.COM 	}
13517697SMichael.Christensen@Sun.COM 
13527697SMichael.Christensen@Sun.COM 	/* make sure the message makes sense */
13537697SMichael.Christensen@Sun.COM 	if (svc->state != DS_SVC_REG_PENDING) {
135410042SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: '%s' handle: 0x%llx "
135510042SMichael.Christensen@Sun.COM 		    "invalid state (%d)" DS_EOL, PORTID(port), svc->cap.svc_id,
135610042SMichael.Christensen@Sun.COM 		    (u_longlong_t)nack->svc_handle, svc->state);
13577697SMichael.Christensen@Sun.COM 		goto done;
13587697SMichael.Christensen@Sun.COM 	}
13597697SMichael.Christensen@Sun.COM 
13607697SMichael.Christensen@Sun.COM 	if (nack->result == DS_REG_DUP) {
13617697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <reg_nack: duplicate registration "
13627697SMichael.Christensen@Sun.COM 		    " for %s" DS_EOL, PORTID(port), svc->cap.svc_id);
13637697SMichael.Christensen@Sun.COM 		ds_reset_svc(svc, port);
13647697SMichael.Christensen@Sun.COM 		goto done;
13657697SMichael.Christensen@Sun.COM 	}
13667697SMichael.Christensen@Sun.COM 
13677697SMichael.Christensen@Sun.COM 	/*
13687697SMichael.Christensen@Sun.COM 	 * A major version of zero indicates that the
13697697SMichael.Christensen@Sun.COM 	 * service is not supported at all.
13707697SMichael.Christensen@Sun.COM 	 */
13717697SMichael.Christensen@Sun.COM 	if (nack->major_vers == 0) {
13727697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: '%s' not supported"
13737697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), svc->cap.svc_id);
13747697SMichael.Christensen@Sun.COM 		ds_reset_svc(svc, port);
13757697SMichael.Christensen@Sun.COM 		if ((svc->flags & DSSF_ISCLIENT) == 0)
13767697SMichael.Christensen@Sun.COM 			ds_try_next_port(svc, PORTID(port) + 1);
13777697SMichael.Christensen@Sun.COM 		goto done;
13787697SMichael.Christensen@Sun.COM 	}
13797697SMichael.Christensen@Sun.COM 
13807697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: '%s' hdl=0x%llx, nack=%d.x"
13817697SMichael.Christensen@Sun.COM 	    DS_EOL, PORTID(port), svc->cap.svc_id,
13827697SMichael.Christensen@Sun.COM 	    (u_longlong_t)nack->svc_handle, nack->major_vers);
13837697SMichael.Christensen@Sun.COM 
13847697SMichael.Christensen@Sun.COM 	/*
13857697SMichael.Christensen@Sun.COM 	 * Walk the version list for the service, looking for
13867697SMichael.Christensen@Sun.COM 	 * a major version that is as close to the requested
13877697SMichael.Christensen@Sun.COM 	 * major version as possible.
13887697SMichael.Christensen@Sun.COM 	 */
13897697SMichael.Christensen@Sun.COM 	for (idx = svc->ver_idx; idx < svc->cap.nvers; idx++) {
13907697SMichael.Christensen@Sun.COM 		if (svc->cap.vers[idx].major <= nack->major_vers) {
13917697SMichael.Christensen@Sun.COM 			/* found a version to try */
13927697SMichael.Christensen@Sun.COM 			break;
13937697SMichael.Christensen@Sun.COM 		}
13947697SMichael.Christensen@Sun.COM 	}
13957697SMichael.Christensen@Sun.COM 
13967697SMichael.Christensen@Sun.COM 	if (idx == svc->cap.nvers) {
13977697SMichael.Christensen@Sun.COM 		/* no supported version */
13987697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: %s v%d.x not supported"
13997697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), svc->cap.svc_id, nack->major_vers);
14007697SMichael.Christensen@Sun.COM 		ds_reset_svc(svc, port);
14017697SMichael.Christensen@Sun.COM 		if ((svc->flags & DSSF_ISCLIENT) == 0)
14027697SMichael.Christensen@Sun.COM 			ds_try_next_port(svc, PORTID(port) + 1);
14037697SMichael.Christensen@Sun.COM 		goto done;
14047697SMichael.Christensen@Sun.COM 	}
14057697SMichael.Christensen@Sun.COM 
14067697SMichael.Christensen@Sun.COM 	/* start the handshake again */
14077697SMichael.Christensen@Sun.COM 	svc->state = DS_SVC_INACTIVE;
14087697SMichael.Christensen@Sun.COM 	svc->ver_idx = idx;
14097697SMichael.Christensen@Sun.COM 
14107697SMichael.Christensen@Sun.COM 	(void) ds_svc_register(svc, NULL);
14117697SMichael.Christensen@Sun.COM 
14127697SMichael.Christensen@Sun.COM done:
14137697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
14147697SMichael.Christensen@Sun.COM }
14157697SMichael.Christensen@Sun.COM 
14167697SMichael.Christensen@Sun.COM static void
ds_handle_unreg_req(ds_port_t * port,caddr_t buf,size_t len)14177697SMichael.Christensen@Sun.COM ds_handle_unreg_req(ds_port_t *port, caddr_t buf, size_t len)
14187697SMichael.Christensen@Sun.COM {
14197697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
14207697SMichael.Christensen@Sun.COM 	ds_unreg_req_t	*req;
14217697SMichael.Christensen@Sun.COM 	ds_unreg_ack_t	*ack;
14227697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
14237697SMichael.Christensen@Sun.COM 	char		*msg;
14247697SMichael.Christensen@Sun.COM 	size_t		msglen;
14257697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_unreg_req_t);
14269916SMichael.Christensen@Sun.COM 	boolean_t	is_up;
14277697SMichael.Christensen@Sun.COM 
14287697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
14297697SMichael.Christensen@Sun.COM 	if (len != explen) {
14307697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <unreg_req: invalid message "
14317697SMichael.Christensen@Sun.COM 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
14327697SMichael.Christensen@Sun.COM 		    explen);
14337697SMichael.Christensen@Sun.COM 		return;
14347697SMichael.Christensen@Sun.COM 	}
14357697SMichael.Christensen@Sun.COM 
14367697SMichael.Christensen@Sun.COM 	req = (ds_unreg_req_t *)(buf + DS_HDR_SZ);
14377697SMichael.Christensen@Sun.COM 
14387697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
14397697SMichael.Christensen@Sun.COM 
14407697SMichael.Christensen@Sun.COM 	/* lookup appropriate client or service */
14417697SMichael.Christensen@Sun.COM 	if (DS_HDL_ISCLIENT(req->svc_handle) ||
14427697SMichael.Christensen@Sun.COM 	    ((svc = ds_find_clnt_svc_by_hdl_port(req->svc_handle, port))
14437697SMichael.Christensen@Sun.COM 	    == NULL && ((svc = ds_get_svc(req->svc_handle)) == NULL ||
14447697SMichael.Christensen@Sun.COM 	    svc->port != port))) {
14459916SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
14469916SMichael.Christensen@Sun.COM 		mutex_enter(&port->lock);
14479916SMichael.Christensen@Sun.COM 		is_up = (port->ldc.state == LDC_UP);
14489916SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
14499916SMichael.Christensen@Sun.COM 		if (!is_up)
14509916SMichael.Christensen@Sun.COM 			return;
145110042SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_req: invalid handle 0x%llx"
14527697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), (u_longlong_t)req->svc_handle);
14537697SMichael.Christensen@Sun.COM 		ds_send_unreg_nack(port, req->svc_handle);
14547697SMichael.Christensen@Sun.COM 		return;
14557697SMichael.Christensen@Sun.COM 	}
14567697SMichael.Christensen@Sun.COM 
14577697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_req: '%s' handle 0x%llx" DS_EOL,
14587697SMichael.Christensen@Sun.COM 	    PORTID(port), svc->cap.svc_id, (u_longlong_t)req->svc_handle);
14597697SMichael.Christensen@Sun.COM 
14607697SMichael.Christensen@Sun.COM 	(void) ds_svc_unregister(svc, svc->port);
14617697SMichael.Christensen@Sun.COM 
14627697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: unreg_ack>: '%s' hdl=0x%llx" DS_EOL,
14637697SMichael.Christensen@Sun.COM 	    PORTID(port), svc->cap.svc_id, (u_longlong_t)req->svc_handle);
14647697SMichael.Christensen@Sun.COM 
14657697SMichael.Christensen@Sun.COM 	ds_check_for_dup_services(svc);
14667697SMichael.Christensen@Sun.COM 
14677697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
14687697SMichael.Christensen@Sun.COM 
14697697SMichael.Christensen@Sun.COM 	msglen = DS_HDR_SZ + sizeof (ds_unreg_ack_t);
14707697SMichael.Christensen@Sun.COM 	msg = DS_MALLOC(msglen);
14717697SMichael.Christensen@Sun.COM 
14727697SMichael.Christensen@Sun.COM 	hdr = (ds_hdr_t *)msg;
14737697SMichael.Christensen@Sun.COM 	hdr->msg_type = DS_UNREG_ACK;
14747697SMichael.Christensen@Sun.COM 	hdr->payload_len = sizeof (ds_unreg_ack_t);
14757697SMichael.Christensen@Sun.COM 
14767697SMichael.Christensen@Sun.COM 	ack = (ds_unreg_ack_t *)(msg + DS_HDR_SZ);
14777697SMichael.Christensen@Sun.COM 	ack->svc_handle = req->svc_handle;
14787697SMichael.Christensen@Sun.COM 
14797697SMichael.Christensen@Sun.COM 	/* send message */
14807697SMichael.Christensen@Sun.COM 	(void) ds_send_msg(port, msg, msglen);
14817697SMichael.Christensen@Sun.COM 	DS_FREE(msg, msglen);
14827697SMichael.Christensen@Sun.COM 
14837697SMichael.Christensen@Sun.COM }
14847697SMichael.Christensen@Sun.COM 
14857697SMichael.Christensen@Sun.COM static void
ds_handle_unreg_ack(ds_port_t * port,caddr_t buf,size_t len)14867697SMichael.Christensen@Sun.COM ds_handle_unreg_ack(ds_port_t *port, caddr_t buf, size_t len)
14877697SMichael.Christensen@Sun.COM {
14887697SMichael.Christensen@Sun.COM 	ds_unreg_ack_t	*ack;
14897697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_unreg_ack_t);
14907697SMichael.Christensen@Sun.COM 
14917697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
14927697SMichael.Christensen@Sun.COM 	if (len != explen) {
14937697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <unreg_ack: invalid message "
14947697SMichael.Christensen@Sun.COM 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
14957697SMichael.Christensen@Sun.COM 		    explen);
14967697SMichael.Christensen@Sun.COM 		return;
14977697SMichael.Christensen@Sun.COM 	}
14987697SMichael.Christensen@Sun.COM 
14997697SMichael.Christensen@Sun.COM 	ack = (ds_unreg_ack_t *)(buf + DS_HDR_SZ);
15007697SMichael.Christensen@Sun.COM 
15017697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_ack: hdl=0x%llx" DS_EOL,
15027697SMichael.Christensen@Sun.COM 	    PORTID(port), (u_longlong_t)ack->svc_handle);
15037697SMichael.Christensen@Sun.COM 
15048894SMichael.Christensen@Sun.COM #ifdef DEBUG
15057697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
15067697SMichael.Christensen@Sun.COM 
15077697SMichael.Christensen@Sun.COM 	/*
15087697SMichael.Christensen@Sun.COM 	 * Since the unregister request was initiated locally,
15097697SMichael.Christensen@Sun.COM 	 * the service structure has already been torn down.
15107697SMichael.Christensen@Sun.COM 	 * Just perform a sanity check to make sure the message
15117697SMichael.Christensen@Sun.COM 	 * is appropriate.
15127697SMichael.Christensen@Sun.COM 	 */
15137697SMichael.Christensen@Sun.COM 	if (ds_get_svc(ack->svc_handle) != NULL) {
15148894SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_ack: handle 0x%llx in use"
15157697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), (u_longlong_t)ack->svc_handle);
15167697SMichael.Christensen@Sun.COM 	}
15177697SMichael.Christensen@Sun.COM 
15187697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
15198894SMichael.Christensen@Sun.COM #endif	/* DEBUG */
15207697SMichael.Christensen@Sun.COM }
15217697SMichael.Christensen@Sun.COM 
15227697SMichael.Christensen@Sun.COM static void
ds_handle_unreg_nack(ds_port_t * port,caddr_t buf,size_t len)15237697SMichael.Christensen@Sun.COM ds_handle_unreg_nack(ds_port_t *port, caddr_t buf, size_t len)
15247697SMichael.Christensen@Sun.COM {
15257697SMichael.Christensen@Sun.COM 	ds_unreg_nack_t	*nack;
15267697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_unreg_nack_t);
15277697SMichael.Christensen@Sun.COM 
15287697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
15297697SMichael.Christensen@Sun.COM 	if (len != explen) {
15307697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <unreg_nack: invalid message "
15317697SMichael.Christensen@Sun.COM 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
15327697SMichael.Christensen@Sun.COM 		    explen);
15337697SMichael.Christensen@Sun.COM 		return;
15347697SMichael.Christensen@Sun.COM 	}
15357697SMichael.Christensen@Sun.COM 
15367697SMichael.Christensen@Sun.COM 	nack = (ds_unreg_nack_t *)(buf + DS_HDR_SZ);
15377697SMichael.Christensen@Sun.COM 
15387697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_nack: hdl=0x%llx" DS_EOL,
15397697SMichael.Christensen@Sun.COM 	    PORTID(port), (u_longlong_t)nack->svc_handle);
15407697SMichael.Christensen@Sun.COM 
15418894SMichael.Christensen@Sun.COM #ifdef DEBUG
15427697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
15437697SMichael.Christensen@Sun.COM 
15447697SMichael.Christensen@Sun.COM 	/*
15457697SMichael.Christensen@Sun.COM 	 * Since the unregister request was initiated locally,
15467697SMichael.Christensen@Sun.COM 	 * the service structure has already been torn down.
15477697SMichael.Christensen@Sun.COM 	 * Just perform a sanity check to make sure the message
15487697SMichael.Christensen@Sun.COM 	 * is appropriate.
15497697SMichael.Christensen@Sun.COM 	 */
15507697SMichael.Christensen@Sun.COM 	if (ds_get_svc(nack->svc_handle) != NULL) {
15518894SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_nack: handle 0x%llx in use"
15527697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle);
15537697SMichael.Christensen@Sun.COM 	}
15547697SMichael.Christensen@Sun.COM 
15557697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
15568894SMichael.Christensen@Sun.COM #endif	/* DEBUG */
15577697SMichael.Christensen@Sun.COM }
15587697SMichael.Christensen@Sun.COM 
15597697SMichael.Christensen@Sun.COM static void
ds_handle_data(ds_port_t * port,caddr_t buf,size_t len)15607697SMichael.Christensen@Sun.COM ds_handle_data(ds_port_t *port, caddr_t buf, size_t len)
15617697SMichael.Christensen@Sun.COM {
15627697SMichael.Christensen@Sun.COM 	ds_data_handle_t	*data;
15637697SMichael.Christensen@Sun.COM 	ds_svc_t		*svc;
15647697SMichael.Christensen@Sun.COM 	char			*msg;
15657697SMichael.Christensen@Sun.COM 	int			msgsz;
15667697SMichael.Christensen@Sun.COM 	int			hdrsz;
15677697SMichael.Christensen@Sun.COM 	size_t			explen = DS_MSG_LEN(ds_data_handle_t);
15687697SMichael.Christensen@Sun.COM 
15697697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
15707697SMichael.Christensen@Sun.COM 	if (len < explen) {
15717697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <data: invalid message length "
15727697SMichael.Christensen@Sun.COM 		    "(%ld), expected at least %ld" DS_EOL, PORTID(port), len,
15737697SMichael.Christensen@Sun.COM 		    explen);
15747697SMichael.Christensen@Sun.COM 		return;
15757697SMichael.Christensen@Sun.COM 	}
15767697SMichael.Christensen@Sun.COM 
15777697SMichael.Christensen@Sun.COM 	data = (ds_data_handle_t *)(buf + DS_HDR_SZ);
15787697SMichael.Christensen@Sun.COM 
15797697SMichael.Christensen@Sun.COM 	hdrsz = DS_HDR_SZ + sizeof (ds_data_handle_t);
15807697SMichael.Christensen@Sun.COM 	msgsz = len - hdrsz;
15817697SMichael.Christensen@Sun.COM 
15827697SMichael.Christensen@Sun.COM 	/* strip off the header for the client */
15837697SMichael.Christensen@Sun.COM 	msg = (msgsz) ? (buf + hdrsz) : NULL;
15847697SMichael.Christensen@Sun.COM 
15857697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
15867697SMichael.Christensen@Sun.COM 
15877697SMichael.Christensen@Sun.COM 	if ((svc = ds_find_clnt_svc_by_hdl_port(data->svc_handle, port))
15887697SMichael.Christensen@Sun.COM 	    == NULL) {
15897697SMichael.Christensen@Sun.COM 		if ((svc = ds_get_svc(data->svc_handle)) == NULL) {
15907697SMichael.Christensen@Sun.COM 			mutex_exit(&ds_svcs.lock);
15917697SMichael.Christensen@Sun.COM 			cmn_err(CE_WARN, "ds@%lx: <data: invalid handle 0x%llx"
15927697SMichael.Christensen@Sun.COM 			    DS_EOL, PORTID(port),
15937697SMichael.Christensen@Sun.COM 			    (u_longlong_t)data->svc_handle);
15947697SMichael.Christensen@Sun.COM 			ds_send_data_nack(port, data->svc_handle);
15957697SMichael.Christensen@Sun.COM 			return;
15967697SMichael.Christensen@Sun.COM 		}
15977697SMichael.Christensen@Sun.COM 	}
15987697SMichael.Christensen@Sun.COM 
15997697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
16007697SMichael.Christensen@Sun.COM 
16017697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <data: '%s' hdl=0x%llx" DS_EOL,
16027697SMichael.Christensen@Sun.COM 	    PORTID(port), svc->cap.svc_id, (u_longlong_t)svc->hdl);
16037697SMichael.Christensen@Sun.COM 	DS_DUMP_MSG(DS_DBG_FLAG_PRCL, msg, msgsz);
16047697SMichael.Christensen@Sun.COM 
16057697SMichael.Christensen@Sun.COM 	/* dispatch this message to the client */
16067697SMichael.Christensen@Sun.COM 	(*svc->ops.ds_data_cb)(svc->ops.cb_arg, msg, msgsz);
16077697SMichael.Christensen@Sun.COM }
16087697SMichael.Christensen@Sun.COM 
16097697SMichael.Christensen@Sun.COM static void
ds_handle_nack(ds_port_t * port,caddr_t buf,size_t len)16107697SMichael.Christensen@Sun.COM ds_handle_nack(ds_port_t *port, caddr_t buf, size_t len)
16117697SMichael.Christensen@Sun.COM {
16127697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
16137697SMichael.Christensen@Sun.COM 	ds_data_nack_t	*nack;
16147697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_data_nack_t);
16157697SMichael.Christensen@Sun.COM 
16167697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
16177697SMichael.Christensen@Sun.COM 	if (len != explen) {
16187697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <data_nack: invalid message "
16197697SMichael.Christensen@Sun.COM 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
16207697SMichael.Christensen@Sun.COM 		    explen);
16217697SMichael.Christensen@Sun.COM 		return;
16227697SMichael.Christensen@Sun.COM 	}
16237697SMichael.Christensen@Sun.COM 
16247697SMichael.Christensen@Sun.COM 	nack = (ds_data_nack_t *)(buf + DS_HDR_SZ);
16257697SMichael.Christensen@Sun.COM 
16267697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: data_nack: hdl=0x%llx, result=0x%llx"
16277697SMichael.Christensen@Sun.COM 	    DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle,
16287697SMichael.Christensen@Sun.COM 	    (u_longlong_t)nack->result);
16297697SMichael.Christensen@Sun.COM 
16307697SMichael.Christensen@Sun.COM 	if (nack->result == DS_INV_HDL) {
16317697SMichael.Christensen@Sun.COM 
16327697SMichael.Christensen@Sun.COM 		mutex_enter(&ds_svcs.lock);
16337697SMichael.Christensen@Sun.COM 
16347697SMichael.Christensen@Sun.COM 		if ((svc = ds_find_clnt_svc_by_hdl_port(nack->svc_handle,
16357697SMichael.Christensen@Sun.COM 		    port)) == NULL) {
16367697SMichael.Christensen@Sun.COM 			if ((svc = ds_get_svc(nack->svc_handle)) == NULL) {
16377697SMichael.Christensen@Sun.COM 				mutex_exit(&ds_svcs.lock);
16387697SMichael.Christensen@Sun.COM 				return;
16397697SMichael.Christensen@Sun.COM 			}
16407697SMichael.Christensen@Sun.COM 		}
16417697SMichael.Christensen@Sun.COM 
16427697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <data_nack: handle 0x%llx reported "
16437697SMichael.Christensen@Sun.COM 		    " as invalid" DS_EOL, PORTID(port),
16447697SMichael.Christensen@Sun.COM 		    (u_longlong_t)nack->svc_handle);
16457697SMichael.Christensen@Sun.COM 
16467697SMichael.Christensen@Sun.COM 		(void) ds_svc_unregister(svc, svc->port);
16477697SMichael.Christensen@Sun.COM 
16487697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
16497697SMichael.Christensen@Sun.COM 	}
16507697SMichael.Christensen@Sun.COM }
16517697SMichael.Christensen@Sun.COM 
16527697SMichael.Christensen@Sun.COM /* Initialize the port */
16537697SMichael.Christensen@Sun.COM void
ds_send_init_req(ds_port_t * port)16547697SMichael.Christensen@Sun.COM ds_send_init_req(ds_port_t *port)
16557697SMichael.Christensen@Sun.COM {
16567697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
16577697SMichael.Christensen@Sun.COM 	ds_init_req_t	*init_req;
16587697SMichael.Christensen@Sun.COM 	size_t		msglen;
16597697SMichael.Christensen@Sun.COM 	ds_ver_t	*vers = &ds_vers[port->ver_idx];
16607697SMichael.Christensen@Sun.COM 
16617697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
16627697SMichael.Christensen@Sun.COM 	if (port->state != DS_PORT_LDC_INIT) {
16637697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_req>: invalid state: %d"
16647697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), port->state);
16657697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
16667697SMichael.Christensen@Sun.COM 		return;
16677697SMichael.Christensen@Sun.COM 	}
16687697SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
16697697SMichael.Christensen@Sun.COM 
16707697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_req>: req=v%d.%d" DS_EOL,
16717697SMichael.Christensen@Sun.COM 	    PORTID(port), vers->major, vers->minor);
16727697SMichael.Christensen@Sun.COM 
16737697SMichael.Christensen@Sun.COM 	msglen = DS_HDR_SZ + sizeof (ds_init_req_t);
16747697SMichael.Christensen@Sun.COM 	hdr = DS_MALLOC(msglen);
16757697SMichael.Christensen@Sun.COM 
16767697SMichael.Christensen@Sun.COM 	hdr->msg_type = DS_INIT_REQ;
16777697SMichael.Christensen@Sun.COM 	hdr->payload_len = sizeof (ds_init_req_t);
16787697SMichael.Christensen@Sun.COM 
16797697SMichael.Christensen@Sun.COM 	init_req = (ds_init_req_t *)((caddr_t)hdr + DS_HDR_SZ);
16807697SMichael.Christensen@Sun.COM 	init_req->major_vers = vers->major;
16817697SMichael.Christensen@Sun.COM 	init_req->minor_vers = vers->minor;
16827697SMichael.Christensen@Sun.COM 
16837697SMichael.Christensen@Sun.COM 	if (ds_send_msg(port, (caddr_t)hdr, msglen) == 0) {
16847697SMichael.Christensen@Sun.COM 		/*
16857697SMichael.Christensen@Sun.COM 		 * We've left the port state unlocked over the malloc/send,
16867697SMichael.Christensen@Sun.COM 		 * make sure no one has changed the state under us before
16877697SMichael.Christensen@Sun.COM 		 * we update the state.
16887697SMichael.Christensen@Sun.COM 		 */
16897697SMichael.Christensen@Sun.COM 		mutex_enter(&port->lock);
16907697SMichael.Christensen@Sun.COM 		if (port->state == DS_PORT_LDC_INIT)
16917697SMichael.Christensen@Sun.COM 			port->state = DS_PORT_INIT_REQ;
16927697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
16937697SMichael.Christensen@Sun.COM 	}
16947697SMichael.Christensen@Sun.COM 	DS_FREE(hdr, msglen);
16957697SMichael.Christensen@Sun.COM }
16967697SMichael.Christensen@Sun.COM 
16977697SMichael.Christensen@Sun.COM static int
ds_send_reg_req(ds_svc_t * svc,ds_port_t * port)16987697SMichael.Christensen@Sun.COM ds_send_reg_req(ds_svc_t *svc, ds_port_t *port)
16997697SMichael.Christensen@Sun.COM {
17007697SMichael.Christensen@Sun.COM 	ds_ver_t	*ver;
17017697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
17027697SMichael.Christensen@Sun.COM 	caddr_t		msg;
17037697SMichael.Christensen@Sun.COM 	size_t		msglen;
17047697SMichael.Christensen@Sun.COM 	ds_reg_req_t	*req;
17057697SMichael.Christensen@Sun.COM 	size_t		idlen;
17067697SMichael.Christensen@Sun.COM 	int		rv;
17077697SMichael.Christensen@Sun.COM 
17088172SMichael.Christensen@Sun.COM 	if ((svc->state != DS_SVC_INACTIVE) &&
17098172SMichael.Christensen@Sun.COM 	    ((svc->flags & DSSF_ISCLIENT) == 0)) {
17108172SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_req>: invalid svc state (%d) "
17118172SMichael.Christensen@Sun.COM 		    "for svc '%s'" DS_EOL, PORTID(port), svc->state,
17128172SMichael.Christensen@Sun.COM 		    svc->cap.svc_id);
17138172SMichael.Christensen@Sun.COM 		return (-1);
17148172SMichael.Christensen@Sun.COM 	}
17157697SMichael.Christensen@Sun.COM 
17167697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
17177697SMichael.Christensen@Sun.COM 
17187697SMichael.Christensen@Sun.COM 	/* check on the LDC to Zeus */
17197697SMichael.Christensen@Sun.COM 	if (port->ldc.state != LDC_UP) {
17207697SMichael.Christensen@Sun.COM 		/* can not send message */
17217697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_req>: channel %ld is not up"
17227697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), port->ldc.id);
17237697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
17247697SMichael.Christensen@Sun.COM 		return (-1);
17257697SMichael.Christensen@Sun.COM 	}
17267697SMichael.Christensen@Sun.COM 
17277697SMichael.Christensen@Sun.COM 	/* make sure port is ready */
17287697SMichael.Christensen@Sun.COM 	if (port->state != DS_PORT_READY) {
17297697SMichael.Christensen@Sun.COM 		/* can not send message */
17307697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_req>: port is not ready"
17317697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port));
17327697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
17337697SMichael.Christensen@Sun.COM 		return (-1);
17347697SMichael.Christensen@Sun.COM 	}
17357697SMichael.Christensen@Sun.COM 
17367697SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
17377697SMichael.Christensen@Sun.COM 
17387697SMichael.Christensen@Sun.COM 	/* allocate the message buffer */
17397697SMichael.Christensen@Sun.COM 	idlen = strlen(svc->cap.svc_id);
17407697SMichael.Christensen@Sun.COM 	msglen = DS_HDR_SZ + sizeof (ds_reg_req_t) + idlen;
17417697SMichael.Christensen@Sun.COM 	msg = DS_MALLOC(msglen);
17427697SMichael.Christensen@Sun.COM 
17437697SMichael.Christensen@Sun.COM 	/* copy in the header data */
17447697SMichael.Christensen@Sun.COM 	hdr = (ds_hdr_t *)msg;
17457697SMichael.Christensen@Sun.COM 	hdr->msg_type = DS_REG_REQ;
17467697SMichael.Christensen@Sun.COM 	hdr->payload_len = sizeof (ds_reg_req_t) + idlen;
17477697SMichael.Christensen@Sun.COM 
17487697SMichael.Christensen@Sun.COM 	req = (ds_reg_req_t *)(msg + DS_HDR_SZ);
17497697SMichael.Christensen@Sun.COM 	req->svc_handle = svc->hdl;
17507697SMichael.Christensen@Sun.COM 	ver = &(svc->cap.vers[svc->ver_idx]);
17517697SMichael.Christensen@Sun.COM 	req->major_vers = ver->major;
17527697SMichael.Christensen@Sun.COM 	req->minor_vers = ver->minor;
17537697SMichael.Christensen@Sun.COM 
17547697SMichael.Christensen@Sun.COM 	/* copy in the service id */
17557697SMichael.Christensen@Sun.COM 	(void) memcpy(req->svc_id, svc->cap.svc_id, idlen + 1);
17567697SMichael.Christensen@Sun.COM 
17577697SMichael.Christensen@Sun.COM 	/* send the message */
17587697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_req>: '%s' ver=%d.%d, hdl=0x%llx"
17597697SMichael.Christensen@Sun.COM 	    DS_EOL, PORTID(port), svc->cap.svc_id, ver->major, ver->minor,
17607697SMichael.Christensen@Sun.COM 	    (u_longlong_t)svc->hdl);
17617697SMichael.Christensen@Sun.COM 
17627697SMichael.Christensen@Sun.COM 	if ((rv = ds_send_msg(port, msg, msglen)) != 0) {
17637697SMichael.Christensen@Sun.COM 		svc->port = port;
17647697SMichael.Christensen@Sun.COM 		rv = -1;
17657697SMichael.Christensen@Sun.COM 	} else if ((svc->flags & DSSF_ISCLIENT) == 0) {
17667697SMichael.Christensen@Sun.COM 		svc->state = DS_SVC_REG_PENDING;
17677697SMichael.Christensen@Sun.COM 	}
17687697SMichael.Christensen@Sun.COM 	DS_FREE(msg, msglen);
17697697SMichael.Christensen@Sun.COM 
17707697SMichael.Christensen@Sun.COM 	return (rv);
17717697SMichael.Christensen@Sun.COM }
17727697SMichael.Christensen@Sun.COM 
17737697SMichael.Christensen@Sun.COM /*
17747697SMichael.Christensen@Sun.COM  * Keep around in case we want this later
17757697SMichael.Christensen@Sun.COM  */
17767697SMichael.Christensen@Sun.COM int
ds_send_unreg_req(ds_svc_t * svc)17777697SMichael.Christensen@Sun.COM ds_send_unreg_req(ds_svc_t *svc)
17787697SMichael.Christensen@Sun.COM {
17797697SMichael.Christensen@Sun.COM 	caddr_t		msg;
17807697SMichael.Christensen@Sun.COM 	size_t		msglen;
17817697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
17827697SMichael.Christensen@Sun.COM 	ds_unreg_req_t	*req;
17837697SMichael.Christensen@Sun.COM 	ds_port_t	*port = svc->port;
17847697SMichael.Christensen@Sun.COM 	int		rv;
17857697SMichael.Christensen@Sun.COM 
17867697SMichael.Christensen@Sun.COM 	if (port == NULL) {
17877697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "send_unreg_req: service '%s' not "
17887697SMichael.Christensen@Sun.COM 		    "associated with a port" DS_EOL, svc->cap.svc_id);
17897697SMichael.Christensen@Sun.COM 		return (-1);
17907697SMichael.Christensen@Sun.COM 	}
17917697SMichael.Christensen@Sun.COM 
17927697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
17937697SMichael.Christensen@Sun.COM 
17947697SMichael.Christensen@Sun.COM 	/* check on the LDC to Zeus */
17957697SMichael.Christensen@Sun.COM 	if (port->ldc.state != LDC_UP) {
17967697SMichael.Christensen@Sun.COM 		/* can not send message */
17977697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: unreg_req>: channel %ld is not up"
17987697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), port->ldc.id);
17997697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
18007697SMichael.Christensen@Sun.COM 		return (-1);
18017697SMichael.Christensen@Sun.COM 	}
18027697SMichael.Christensen@Sun.COM 
18037697SMichael.Christensen@Sun.COM 	/* make sure port is ready */
18047697SMichael.Christensen@Sun.COM 	if (port->state != DS_PORT_READY) {
18057697SMichael.Christensen@Sun.COM 		/* can not send message */
18067697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: unreg_req>: port is not ready" DS_EOL,
18077697SMichael.Christensen@Sun.COM 		    PORTID(port));
18087697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
18097697SMichael.Christensen@Sun.COM 		return (-1);
18107697SMichael.Christensen@Sun.COM 	}
18117697SMichael.Christensen@Sun.COM 
18127697SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
18137697SMichael.Christensen@Sun.COM 
18147697SMichael.Christensen@Sun.COM 	msglen = DS_HDR_SZ + sizeof (ds_unreg_req_t);
18157697SMichael.Christensen@Sun.COM 	msg = DS_MALLOC(msglen);
18167697SMichael.Christensen@Sun.COM 
18177697SMichael.Christensen@Sun.COM 	/* copy in the header data */
18187697SMichael.Christensen@Sun.COM 	hdr = (ds_hdr_t *)msg;
18197697SMichael.Christensen@Sun.COM 	hdr->msg_type = DS_UNREG;
18207697SMichael.Christensen@Sun.COM 	hdr->payload_len = sizeof (ds_unreg_req_t);
18217697SMichael.Christensen@Sun.COM 
18227697SMichael.Christensen@Sun.COM 	req = (ds_unreg_req_t *)(msg + DS_HDR_SZ);
18237697SMichael.Christensen@Sun.COM 	if (svc->flags & DSSF_ISCLIENT) {
18247697SMichael.Christensen@Sun.COM 		req->svc_handle = svc->svc_hdl;
18257697SMichael.Christensen@Sun.COM 	} else {
18267697SMichael.Christensen@Sun.COM 		req->svc_handle = svc->hdl;
18277697SMichael.Christensen@Sun.COM 	}
18287697SMichael.Christensen@Sun.COM 
18297697SMichael.Christensen@Sun.COM 	/* send the message */
18307697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: unreg_req>: '%s' hdl=0x%llx" DS_EOL,
18317697SMichael.Christensen@Sun.COM 	    PORTID(port), (svc->cap.svc_id) ? svc->cap.svc_id : "NULL",
18327697SMichael.Christensen@Sun.COM 	    (u_longlong_t)svc->hdl);
18337697SMichael.Christensen@Sun.COM 
18347697SMichael.Christensen@Sun.COM 	if ((rv = ds_send_msg(port, msg, msglen)) != 0) {
18357697SMichael.Christensen@Sun.COM 		rv = -1;
18367697SMichael.Christensen@Sun.COM 	}
18377697SMichael.Christensen@Sun.COM 	DS_FREE(msg, msglen);
18387697SMichael.Christensen@Sun.COM 
18397697SMichael.Christensen@Sun.COM 	return (rv);
18407697SMichael.Christensen@Sun.COM }
18417697SMichael.Christensen@Sun.COM 
18427697SMichael.Christensen@Sun.COM static void
ds_send_unreg_nack(ds_port_t * port,ds_svc_hdl_t bad_hdl)18437697SMichael.Christensen@Sun.COM ds_send_unreg_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl)
18447697SMichael.Christensen@Sun.COM {
18457697SMichael.Christensen@Sun.COM 	caddr_t		msg;
18467697SMichael.Christensen@Sun.COM 	size_t		msglen;
18477697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
18487697SMichael.Christensen@Sun.COM 	ds_unreg_nack_t	*nack;
18497697SMichael.Christensen@Sun.COM 
18507697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
18517697SMichael.Christensen@Sun.COM 
18527697SMichael.Christensen@Sun.COM 	/* check on the LDC to Zeus */
18537697SMichael.Christensen@Sun.COM 	if (port->ldc.state != LDC_UP) {
18547697SMichael.Christensen@Sun.COM 		/* can not send message */
18557697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: unreg_nack>: channel %ld is not up"
18567697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), port->ldc.id);
18577697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
18587697SMichael.Christensen@Sun.COM 		return;
18597697SMichael.Christensen@Sun.COM 	}
18607697SMichael.Christensen@Sun.COM 
18617697SMichael.Christensen@Sun.COM 	/* make sure port is ready */
18627697SMichael.Christensen@Sun.COM 	if (port->state != DS_PORT_READY) {
18637697SMichael.Christensen@Sun.COM 		/* can not send message */
18647697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: unreg_nack>: port is not ready"
18657697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port));
18667697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
18677697SMichael.Christensen@Sun.COM 		return;
18687697SMichael.Christensen@Sun.COM 	}
18697697SMichael.Christensen@Sun.COM 
18707697SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
18717697SMichael.Christensen@Sun.COM 
18727697SMichael.Christensen@Sun.COM 	msglen = DS_HDR_SZ + sizeof (ds_unreg_nack_t);
18737697SMichael.Christensen@Sun.COM 	msg = DS_MALLOC(msglen);
18747697SMichael.Christensen@Sun.COM 
18757697SMichael.Christensen@Sun.COM 	/* copy in the header data */
18767697SMichael.Christensen@Sun.COM 	hdr = (ds_hdr_t *)msg;
18777697SMichael.Christensen@Sun.COM 	hdr->msg_type = DS_UNREG_NACK;
18787697SMichael.Christensen@Sun.COM 	hdr->payload_len = sizeof (ds_unreg_nack_t);
18797697SMichael.Christensen@Sun.COM 
18807697SMichael.Christensen@Sun.COM 	nack = (ds_unreg_nack_t *)(msg + DS_HDR_SZ);
18817697SMichael.Christensen@Sun.COM 	nack->svc_handle = bad_hdl;
18827697SMichael.Christensen@Sun.COM 
18837697SMichael.Christensen@Sun.COM 	/* send the message */
18847697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: unreg_nack>: hdl=0x%llx" DS_EOL,
18857697SMichael.Christensen@Sun.COM 	    PORTID(port), (u_longlong_t)bad_hdl);
18867697SMichael.Christensen@Sun.COM 
18877697SMichael.Christensen@Sun.COM 	(void) ds_send_msg(port, msg, msglen);
18887697SMichael.Christensen@Sun.COM 	DS_FREE(msg, msglen);
18897697SMichael.Christensen@Sun.COM }
18907697SMichael.Christensen@Sun.COM 
18917697SMichael.Christensen@Sun.COM static void
ds_send_data_nack(ds_port_t * port,ds_svc_hdl_t bad_hdl)18927697SMichael.Christensen@Sun.COM ds_send_data_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl)
18937697SMichael.Christensen@Sun.COM {
18947697SMichael.Christensen@Sun.COM 	caddr_t		msg;
18957697SMichael.Christensen@Sun.COM 	size_t		msglen;
18967697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
18977697SMichael.Christensen@Sun.COM 	ds_data_nack_t	*nack;
18987697SMichael.Christensen@Sun.COM 
18997697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
19007697SMichael.Christensen@Sun.COM 
19017697SMichael.Christensen@Sun.COM 	/* check on the LDC to Zeus */
19027697SMichael.Christensen@Sun.COM 	if (port->ldc.state != LDC_UP) {
19037697SMichael.Christensen@Sun.COM 		/* can not send message */
19047697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: data_nack>: channel %ld is not up"
19057697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), port->ldc.id);
19067697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
19077697SMichael.Christensen@Sun.COM 		return;
19087697SMichael.Christensen@Sun.COM 	}
19097697SMichael.Christensen@Sun.COM 
19107697SMichael.Christensen@Sun.COM 	/* make sure port is ready */
19117697SMichael.Christensen@Sun.COM 	if (port->state != DS_PORT_READY) {
19127697SMichael.Christensen@Sun.COM 		/* can not send message */
19137697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: data_nack>: port is not ready" DS_EOL,
19147697SMichael.Christensen@Sun.COM 		    PORTID(port));
19157697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
19167697SMichael.Christensen@Sun.COM 		return;
19177697SMichael.Christensen@Sun.COM 	}
19187697SMichael.Christensen@Sun.COM 
19197697SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
19207697SMichael.Christensen@Sun.COM 
19217697SMichael.Christensen@Sun.COM 	msglen = DS_HDR_SZ + sizeof (ds_data_nack_t);
19227697SMichael.Christensen@Sun.COM 	msg = DS_MALLOC(msglen);
19237697SMichael.Christensen@Sun.COM 
19247697SMichael.Christensen@Sun.COM 	/* copy in the header data */
19257697SMichael.Christensen@Sun.COM 	hdr = (ds_hdr_t *)msg;
19267697SMichael.Christensen@Sun.COM 	hdr->msg_type = DS_NACK;
19277697SMichael.Christensen@Sun.COM 	hdr->payload_len = sizeof (ds_data_nack_t);
19287697SMichael.Christensen@Sun.COM 
19297697SMichael.Christensen@Sun.COM 	nack = (ds_data_nack_t *)(msg + DS_HDR_SZ);
19307697SMichael.Christensen@Sun.COM 	nack->svc_handle = bad_hdl;
19317697SMichael.Christensen@Sun.COM 	nack->result = DS_INV_HDL;
19327697SMichael.Christensen@Sun.COM 
19337697SMichael.Christensen@Sun.COM 	/* send the message */
19347697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: data_nack>: hdl=0x%llx" DS_EOL,
19357697SMichael.Christensen@Sun.COM 	    PORTID(port), (u_longlong_t)bad_hdl);
19367697SMichael.Christensen@Sun.COM 
19377697SMichael.Christensen@Sun.COM 	(void) ds_send_msg(port, msg, msglen);
19387697SMichael.Christensen@Sun.COM 	DS_FREE(msg, msglen);
19397697SMichael.Christensen@Sun.COM }
19407697SMichael.Christensen@Sun.COM 
19417697SMichael.Christensen@Sun.COM /* END DS PROTOCOL SUPPORT FUNCTIONS */
19427697SMichael.Christensen@Sun.COM 
19437697SMichael.Christensen@Sun.COM #ifdef DEBUG
19447697SMichael.Christensen@Sun.COM 
19457697SMichael.Christensen@Sun.COM #define	BYTESPERLINE	8
19467697SMichael.Christensen@Sun.COM #define	LINEWIDTH	((BYTESPERLINE * 3) + (BYTESPERLINE + 2) + 1)
19477697SMichael.Christensen@Sun.COM #define	ASCIIOFFSET	((BYTESPERLINE * 3) + 2)
19487697SMichael.Christensen@Sun.COM #define	ISPRINT(c)	((c >= ' ') && (c <= '~'))
19497697SMichael.Christensen@Sun.COM 
19507697SMichael.Christensen@Sun.COM /*
19517697SMichael.Christensen@Sun.COM  * Output a buffer formatted with a set number of bytes on
19527697SMichael.Christensen@Sun.COM  * each line. Append each line with the ASCII equivalent of
19537697SMichael.Christensen@Sun.COM  * each byte if it falls within the printable ASCII range,
19547697SMichael.Christensen@Sun.COM  * and '.' otherwise.
19557697SMichael.Christensen@Sun.COM  */
19567697SMichael.Christensen@Sun.COM void
ds_dump_msg(void * vbuf,size_t len)19577697SMichael.Christensen@Sun.COM ds_dump_msg(void *vbuf, size_t len)
19587697SMichael.Christensen@Sun.COM {
19597697SMichael.Christensen@Sun.COM 	int	i, j;
19607697SMichael.Christensen@Sun.COM 	char	*curr;
19617697SMichael.Christensen@Sun.COM 	char	*aoff;
19627697SMichael.Christensen@Sun.COM 	char	line[LINEWIDTH];
19637697SMichael.Christensen@Sun.COM 	uint8_t	*buf = vbuf;
19647697SMichael.Christensen@Sun.COM 
19657697SMichael.Christensen@Sun.COM 	if (len > 128)
19667697SMichael.Christensen@Sun.COM 		len = 128;
19677697SMichael.Christensen@Sun.COM 
19687697SMichael.Christensen@Sun.COM 	/* walk the buffer one line at a time */
19697697SMichael.Christensen@Sun.COM 	for (i = 0; i < len; i += BYTESPERLINE) {
19707697SMichael.Christensen@Sun.COM 
19717697SMichael.Christensen@Sun.COM 		bzero(line, LINEWIDTH);
19727697SMichael.Christensen@Sun.COM 
19737697SMichael.Christensen@Sun.COM 		curr = line;
19747697SMichael.Christensen@Sun.COM 		aoff = line + ASCIIOFFSET;
19757697SMichael.Christensen@Sun.COM 
19767697SMichael.Christensen@Sun.COM 		/*
19777697SMichael.Christensen@Sun.COM 		 * Walk the bytes in the current line, storing
19787697SMichael.Christensen@Sun.COM 		 * the hex value for the byte as well as the
19797697SMichael.Christensen@Sun.COM 		 * ASCII representation in a temporary buffer.
19807697SMichael.Christensen@Sun.COM 		 * All ASCII values are placed at the end of
19817697SMichael.Christensen@Sun.COM 		 * the line.
19827697SMichael.Christensen@Sun.COM 		 */
19837697SMichael.Christensen@Sun.COM 		for (j = 0; (j < BYTESPERLINE) && ((i + j) < len); j++) {
19847697SMichael.Christensen@Sun.COM 			(void) sprintf(curr, " %02x", buf[i + j]);
19857697SMichael.Christensen@Sun.COM 			*aoff = (ISPRINT(buf[i + j])) ? buf[i + j] : '.';
19867697SMichael.Christensen@Sun.COM 			curr += 3;
19877697SMichael.Christensen@Sun.COM 			aoff++;
19887697SMichael.Christensen@Sun.COM 		}
19897697SMichael.Christensen@Sun.COM 
19907697SMichael.Christensen@Sun.COM 		/*
19917697SMichael.Christensen@Sun.COM 		 * Fill in to the start of the ASCII translation
19927697SMichael.Christensen@Sun.COM 		 * with spaces. This will only be necessary if
19937697SMichael.Christensen@Sun.COM 		 * this is the last line and there are not enough
19947697SMichael.Christensen@Sun.COM 		 * bytes to fill the whole line.
19957697SMichael.Christensen@Sun.COM 		 */
19967697SMichael.Christensen@Sun.COM 		while (curr != (line + ASCIIOFFSET))
19977697SMichael.Christensen@Sun.COM 			*curr++ = ' ';
19987697SMichael.Christensen@Sun.COM 
19997697SMichael.Christensen@Sun.COM 		cmn_err(CE_NOTE, "%s" DS_EOL, line);
20007697SMichael.Christensen@Sun.COM 	}
20017697SMichael.Christensen@Sun.COM }
20027697SMichael.Christensen@Sun.COM #endif /* DEBUG */
20037697SMichael.Christensen@Sun.COM 
20047697SMichael.Christensen@Sun.COM 
20057697SMichael.Christensen@Sun.COM /*
20067697SMichael.Christensen@Sun.COM  * Walk the table of registered services, executing the specified callback
20077697SMichael.Christensen@Sun.COM  * function for each service on a port. A non-zero return value from the
20087697SMichael.Christensen@Sun.COM  * callback is used to terminate the walk, not to indicate an error. Returns
20097697SMichael.Christensen@Sun.COM  * the index of the last service visited.
20107697SMichael.Christensen@Sun.COM  */
20117697SMichael.Christensen@Sun.COM int
ds_walk_svcs(svc_cb_t svc_cb,void * arg)20127697SMichael.Christensen@Sun.COM ds_walk_svcs(svc_cb_t svc_cb, void *arg)
20137697SMichael.Christensen@Sun.COM {
20147697SMichael.Christensen@Sun.COM 	int		idx;
20157697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
20167697SMichael.Christensen@Sun.COM 
20177697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
20187697SMichael.Christensen@Sun.COM 
20197697SMichael.Christensen@Sun.COM 	/* walk every table entry */
20207697SMichael.Christensen@Sun.COM 	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
20217697SMichael.Christensen@Sun.COM 		svc = ds_svcs.tbl[idx];
20227697SMichael.Christensen@Sun.COM 
20237697SMichael.Christensen@Sun.COM 		/* execute the callback */
20247697SMichael.Christensen@Sun.COM 		if ((*svc_cb)(svc, arg) != 0)
20257697SMichael.Christensen@Sun.COM 			break;
20267697SMichael.Christensen@Sun.COM 	}
20277697SMichael.Christensen@Sun.COM 
20287697SMichael.Christensen@Sun.COM 	return (idx);
20297697SMichael.Christensen@Sun.COM }
20307697SMichael.Christensen@Sun.COM 
20317697SMichael.Christensen@Sun.COM static int
ds_svc_isfree(ds_svc_t * svc,void * arg)20327697SMichael.Christensen@Sun.COM ds_svc_isfree(ds_svc_t *svc, void *arg)
20337697SMichael.Christensen@Sun.COM {
20347697SMichael.Christensen@Sun.COM 	_NOTE(ARGUNUSED(arg))
20357697SMichael.Christensen@Sun.COM 
20367697SMichael.Christensen@Sun.COM 	/*
20377697SMichael.Christensen@Sun.COM 	 * Looking for a free service. This may be a NULL entry
20387697SMichael.Christensen@Sun.COM 	 * in the table, or an unused structure that could be
20397697SMichael.Christensen@Sun.COM 	 * reused.
20407697SMichael.Christensen@Sun.COM 	 */
20417697SMichael.Christensen@Sun.COM 
20427697SMichael.Christensen@Sun.COM 	if (DS_SVC_ISFREE(svc)) {
20437697SMichael.Christensen@Sun.COM 		/* yes, it is free */
20447697SMichael.Christensen@Sun.COM 		return (1);
20457697SMichael.Christensen@Sun.COM 	}
20467697SMichael.Christensen@Sun.COM 
20477697SMichael.Christensen@Sun.COM 	/* not a candidate */
20487697SMichael.Christensen@Sun.COM 	return (0);
20497697SMichael.Christensen@Sun.COM }
20507697SMichael.Christensen@Sun.COM 
20517697SMichael.Christensen@Sun.COM int
ds_svc_ismatch(ds_svc_t * svc,void * arg)20527697SMichael.Christensen@Sun.COM ds_svc_ismatch(ds_svc_t *svc, void *arg)
20537697SMichael.Christensen@Sun.COM {
20547697SMichael.Christensen@Sun.COM 	if (DS_SVC_ISFREE(svc)) {
20557697SMichael.Christensen@Sun.COM 		return (0);
20567697SMichael.Christensen@Sun.COM 	}
20577697SMichael.Christensen@Sun.COM 
20587697SMichael.Christensen@Sun.COM 	if (strcmp(svc->cap.svc_id, arg) == 0 &&
20597697SMichael.Christensen@Sun.COM 	    (svc->flags & DSSF_ISCLIENT) == 0) {
20607697SMichael.Christensen@Sun.COM 		/* found a match */
20617697SMichael.Christensen@Sun.COM 		return (1);
20627697SMichael.Christensen@Sun.COM 	}
20637697SMichael.Christensen@Sun.COM 
20647697SMichael.Christensen@Sun.COM 	return (0);
20657697SMichael.Christensen@Sun.COM }
20667697SMichael.Christensen@Sun.COM 
20677697SMichael.Christensen@Sun.COM int
ds_svc_clnt_ismatch(ds_svc_t * svc,void * arg)20687697SMichael.Christensen@Sun.COM ds_svc_clnt_ismatch(ds_svc_t *svc, void *arg)
20697697SMichael.Christensen@Sun.COM {
20707697SMichael.Christensen@Sun.COM 	if (DS_SVC_ISFREE(svc)) {
20717697SMichael.Christensen@Sun.COM 		return (0);
20727697SMichael.Christensen@Sun.COM 	}
20737697SMichael.Christensen@Sun.COM 
20747697SMichael.Christensen@Sun.COM 	if (strcmp(svc->cap.svc_id, arg) == 0 &&
20757697SMichael.Christensen@Sun.COM 	    (svc->flags & DSSF_ISCLIENT) != 0) {
20767697SMichael.Christensen@Sun.COM 		/* found a match */
20777697SMichael.Christensen@Sun.COM 		return (1);
20787697SMichael.Christensen@Sun.COM 	}
20797697SMichael.Christensen@Sun.COM 
20807697SMichael.Christensen@Sun.COM 	return (0);
20817697SMichael.Christensen@Sun.COM }
20827697SMichael.Christensen@Sun.COM 
20837697SMichael.Christensen@Sun.COM int
ds_svc_free(ds_svc_t * svc,void * arg)20847697SMichael.Christensen@Sun.COM ds_svc_free(ds_svc_t *svc, void *arg)
20857697SMichael.Christensen@Sun.COM {
20867697SMichael.Christensen@Sun.COM 	_NOTE(ARGUNUSED(arg))
20877697SMichael.Christensen@Sun.COM 
20887697SMichael.Christensen@Sun.COM 	if (svc == NULL) {
20897697SMichael.Christensen@Sun.COM 		return (0);
20907697SMichael.Christensen@Sun.COM 	}
20917697SMichael.Christensen@Sun.COM 
20927697SMichael.Christensen@Sun.COM 	if (svc->cap.svc_id) {
20937697SMichael.Christensen@Sun.COM 		DS_FREE(svc->cap.svc_id, strlen(svc->cap.svc_id) + 1);
20947697SMichael.Christensen@Sun.COM 		svc->cap.svc_id = NULL;
20957697SMichael.Christensen@Sun.COM 	}
20967697SMichael.Christensen@Sun.COM 
20977697SMichael.Christensen@Sun.COM 	if (svc->cap.vers) {
20987697SMichael.Christensen@Sun.COM 		DS_FREE(svc->cap.vers, svc->cap.nvers * sizeof (ds_ver_t));
20997697SMichael.Christensen@Sun.COM 		svc->cap.vers = NULL;
21007697SMichael.Christensen@Sun.COM 	}
21017697SMichael.Christensen@Sun.COM 
21027697SMichael.Christensen@Sun.COM 	DS_FREE(svc, sizeof (ds_svc_t));
21037697SMichael.Christensen@Sun.COM 
21047697SMichael.Christensen@Sun.COM 	return (0);
21057697SMichael.Christensen@Sun.COM }
21067697SMichael.Christensen@Sun.COM 
210710042SMichael.Christensen@Sun.COM static void
ds_set_svc_port_tried(char * svc_id,ds_port_t * port)210810042SMichael.Christensen@Sun.COM ds_set_svc_port_tried(char *svc_id, ds_port_t *port)
210910042SMichael.Christensen@Sun.COM {
211010042SMichael.Christensen@Sun.COM 	int		idx;
211110042SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
211210042SMichael.Christensen@Sun.COM 
211310042SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
211410042SMichael.Christensen@Sun.COM 
211510042SMichael.Christensen@Sun.COM 	/* walk every table entry */
211610042SMichael.Christensen@Sun.COM 	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
211710042SMichael.Christensen@Sun.COM 		svc = ds_svcs.tbl[idx];
211810042SMichael.Christensen@Sun.COM 		if (!DS_SVC_ISFREE(svc) && (svc->flags & DSSF_ISCLIENT) != 0 &&
211910042SMichael.Christensen@Sun.COM 		    strcmp(svc_id, svc->cap.svc_id) == 0)
212010042SMichael.Christensen@Sun.COM 			DS_PORTSET_ADD(svc->tried, PORTID(port));
212110042SMichael.Christensen@Sun.COM 	}
212210042SMichael.Christensen@Sun.COM }
212310042SMichael.Christensen@Sun.COM 
21247697SMichael.Christensen@Sun.COM static int
ds_svc_register_onport(ds_svc_t * svc,ds_port_t * port)21257697SMichael.Christensen@Sun.COM ds_svc_register_onport(ds_svc_t *svc, ds_port_t *port)
21267697SMichael.Christensen@Sun.COM {
21277697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
21287697SMichael.Christensen@Sun.COM 
21297697SMichael.Christensen@Sun.COM 	if (DS_SVC_ISFREE(svc))
21307697SMichael.Christensen@Sun.COM 		return (0);
21317697SMichael.Christensen@Sun.COM 
21327697SMichael.Christensen@Sun.COM 	if (!DS_PORT_IN_SET(svc->avail, PORTID(port)))
21337697SMichael.Christensen@Sun.COM 		return (0);
21347697SMichael.Christensen@Sun.COM 
213510042SMichael.Christensen@Sun.COM 	if (DS_PORT_IN_SET(svc->tried, PORTID(port)))
213610042SMichael.Christensen@Sun.COM 		return (0);
213710042SMichael.Christensen@Sun.COM 
2138*12231SMichael.Christensen@Sun.COM 	if (!ds_port_is_ready(port))
2139*12231SMichael.Christensen@Sun.COM 		return (0);
2140*12231SMichael.Christensen@Sun.COM 
214110042SMichael.Christensen@Sun.COM 	if ((svc->flags & DSSF_ISCLIENT) == 0) {
214210042SMichael.Christensen@Sun.COM 		if (svc->state != DS_SVC_INACTIVE)
214310042SMichael.Christensen@Sun.COM 			return (0);
2144*12231SMichael.Christensen@Sun.COM 		DS_PORTSET_ADD(svc->tried, PORTID(port));
214510042SMichael.Christensen@Sun.COM 	} else {
214610042SMichael.Christensen@Sun.COM 		ds_set_svc_port_tried(svc->cap.svc_id, port);
214710042SMichael.Christensen@Sun.COM 
214810042SMichael.Christensen@Sun.COM 		/*
214910042SMichael.Christensen@Sun.COM 		 * Never send a client reg req to the SP.
215010042SMichael.Christensen@Sun.COM 		 */
215110042SMichael.Christensen@Sun.COM 		if (PORTID(port) == ds_sp_port_id) {
215210042SMichael.Christensen@Sun.COM 			return (0);
215310042SMichael.Christensen@Sun.COM 		}
215410042SMichael.Christensen@Sun.COM 	}
21557697SMichael.Christensen@Sun.COM 
21567697SMichael.Christensen@Sun.COM 	if (ds_send_reg_req(svc, port) == 0) {
21577697SMichael.Christensen@Sun.COM 		/* register sent successfully */
21587697SMichael.Christensen@Sun.COM 		return (1);
21597697SMichael.Christensen@Sun.COM 	}
21607697SMichael.Christensen@Sun.COM 
21617697SMichael.Christensen@Sun.COM 	if ((svc->flags & DSSF_ISCLIENT) == 0) {
21627697SMichael.Christensen@Sun.COM 		/* reset the service */
21637697SMichael.Christensen@Sun.COM 		ds_reset_svc(svc, port);
21647697SMichael.Christensen@Sun.COM 	}
21657697SMichael.Christensen@Sun.COM 	return (0);
21667697SMichael.Christensen@Sun.COM }
21677697SMichael.Christensen@Sun.COM 
216810042SMichael.Christensen@Sun.COM static int
ds_svc_register_onport_walker(ds_svc_t * svc,void * arg)216910042SMichael.Christensen@Sun.COM ds_svc_register_onport_walker(ds_svc_t *svc, void *arg)
217010042SMichael.Christensen@Sun.COM {
217110042SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
217210042SMichael.Christensen@Sun.COM 
217310042SMichael.Christensen@Sun.COM 	if (DS_SVC_ISFREE(svc))
217410042SMichael.Christensen@Sun.COM 		return (0);
217510042SMichael.Christensen@Sun.COM 
217610042SMichael.Christensen@Sun.COM 	(void) ds_svc_register_onport(svc, arg);
217710042SMichael.Christensen@Sun.COM 	return (0);
217810042SMichael.Christensen@Sun.COM }
217910042SMichael.Christensen@Sun.COM 
21807697SMichael.Christensen@Sun.COM int
ds_svc_register(ds_svc_t * svc,void * arg)21817697SMichael.Christensen@Sun.COM ds_svc_register(ds_svc_t *svc, void *arg)
21827697SMichael.Christensen@Sun.COM {
21837697SMichael.Christensen@Sun.COM 	_NOTE(ARGUNUSED(arg))
21847697SMichael.Christensen@Sun.COM 	ds_portset_t ports;
21857697SMichael.Christensen@Sun.COM 	ds_port_t *port;
21867697SMichael.Christensen@Sun.COM 	int	idx;
21877697SMichael.Christensen@Sun.COM 
21887697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
21897697SMichael.Christensen@Sun.COM 
21907697SMichael.Christensen@Sun.COM 	if (DS_SVC_ISFREE(svc))
21917697SMichael.Christensen@Sun.COM 		return (0);
21927697SMichael.Christensen@Sun.COM 
21939535SMichael.Christensen@Sun.COM 	DS_PORTSET_DUP(ports, svc->avail);
21947697SMichael.Christensen@Sun.COM 	if (svc->flags & DSSF_ISCLIENT) {
219510042SMichael.Christensen@Sun.COM 		for (idx = 0; idx < DS_MAX_PORTS; idx++) {
219610042SMichael.Christensen@Sun.COM 			if (DS_PORT_IN_SET(svc->tried, idx))
219710042SMichael.Christensen@Sun.COM 				DS_PORTSET_DEL(ports, idx);
219810042SMichael.Christensen@Sun.COM 		}
21997697SMichael.Christensen@Sun.COM 	} else if (svc->state != DS_SVC_INACTIVE)
22007697SMichael.Christensen@Sun.COM 		return (0);
22017697SMichael.Christensen@Sun.COM 
22027697SMichael.Christensen@Sun.COM 	if (DS_PORTSET_ISNULL(ports))
22037697SMichael.Christensen@Sun.COM 		return (0);
22047697SMichael.Christensen@Sun.COM 
22057697SMichael.Christensen@Sun.COM 	/*
22067697SMichael.Christensen@Sun.COM 	 * Attempt to register the service. Start with the lowest
22077697SMichael.Christensen@Sun.COM 	 * numbered port and continue until a registration message
22087697SMichael.Christensen@Sun.COM 	 * is sent successfully, or there are no ports left to try.
22097697SMichael.Christensen@Sun.COM 	 */
22107697SMichael.Christensen@Sun.COM 	for (idx = 0; idx < DS_MAX_PORTS; idx++) {
22117697SMichael.Christensen@Sun.COM 
22127697SMichael.Christensen@Sun.COM 		/*
22137697SMichael.Christensen@Sun.COM 		 * If the port is not in the available list,
22147697SMichael.Christensen@Sun.COM 		 * it is not a candidate for registration.
22157697SMichael.Christensen@Sun.COM 		 */
22167697SMichael.Christensen@Sun.COM 		if (!DS_PORT_IN_SET(ports, idx)) {
22177697SMichael.Christensen@Sun.COM 			continue;
22187697SMichael.Christensen@Sun.COM 		}
22197697SMichael.Christensen@Sun.COM 
22207697SMichael.Christensen@Sun.COM 		port = &ds_ports[idx];
22217697SMichael.Christensen@Sun.COM 		if (ds_svc_register_onport(svc, port)) {
22227697SMichael.Christensen@Sun.COM 			if ((svc->flags & DSSF_ISCLIENT) == 0)
22237697SMichael.Christensen@Sun.COM 				break;
22247697SMichael.Christensen@Sun.COM 		}
22257697SMichael.Christensen@Sun.COM 	}
22267697SMichael.Christensen@Sun.COM 
22277697SMichael.Christensen@Sun.COM 	return (0);
22287697SMichael.Christensen@Sun.COM }
22297697SMichael.Christensen@Sun.COM 
22307697SMichael.Christensen@Sun.COM static int
ds_svc_unregister(ds_svc_t * svc,void * arg)22317697SMichael.Christensen@Sun.COM ds_svc_unregister(ds_svc_t *svc, void *arg)
22327697SMichael.Christensen@Sun.COM {
22337697SMichael.Christensen@Sun.COM 	ds_port_t *port = (ds_port_t *)arg;
22347697SMichael.Christensen@Sun.COM 	ds_svc_hdl_t hdl;
22357697SMichael.Christensen@Sun.COM 
22367697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
22377697SMichael.Christensen@Sun.COM 
22387697SMichael.Christensen@Sun.COM 	if (DS_SVC_ISFREE(svc)) {
22397697SMichael.Christensen@Sun.COM 		return (0);
22407697SMichael.Christensen@Sun.COM 	}
22417697SMichael.Christensen@Sun.COM 
22427697SMichael.Christensen@Sun.COM 	/* make sure the service is using this port */
22437697SMichael.Christensen@Sun.COM 	if (svc->port != port) {
22447697SMichael.Christensen@Sun.COM 		return (0);
22457697SMichael.Christensen@Sun.COM 	}
22467697SMichael.Christensen@Sun.COM 
22477697SMichael.Christensen@Sun.COM 	if (port) {
22487697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "ds@%lx: svc_unreg: id='%s', ver=%d.%d, "
22497697SMichael.Christensen@Sun.COM 		    " hdl=0x%09lx" DS_EOL, PORTID(port), svc->cap.svc_id,
22507697SMichael.Christensen@Sun.COM 		    svc->ver.major, svc->ver.minor, svc->hdl);
22517697SMichael.Christensen@Sun.COM 	} else {
22527697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "port=NULL: svc_unreg: id='%s', ver=%d.%d, "
22537697SMichael.Christensen@Sun.COM 		    " hdl=0x%09lx" DS_EOL, svc->cap.svc_id, svc->ver.major,
22547697SMichael.Christensen@Sun.COM 		    svc->ver.minor, svc->hdl);
22557697SMichael.Christensen@Sun.COM 	}
22567697SMichael.Christensen@Sun.COM 
22577697SMichael.Christensen@Sun.COM 	/* reset the service structure */
22587697SMichael.Christensen@Sun.COM 	ds_reset_svc(svc, port);
22597697SMichael.Christensen@Sun.COM 
22607697SMichael.Christensen@Sun.COM 	/* call the client unregister callback */
22617697SMichael.Christensen@Sun.COM 	if (svc->ops.ds_unreg_cb) {
22627697SMichael.Christensen@Sun.COM 		(*svc->ops.ds_unreg_cb)(svc->ops.cb_arg);
22637697SMichael.Christensen@Sun.COM 	}
22647697SMichael.Christensen@Sun.COM 
22657697SMichael.Christensen@Sun.COM 	/* increment the count in the handle to prevent reuse */
22667697SMichael.Christensen@Sun.COM 	hdl = DS_ALLOC_HDL(DS_HDL2IDX(svc->hdl), DS_HDL2COUNT(svc->hdl));
22677697SMichael.Christensen@Sun.COM 	if (DS_HDL_ISCLIENT(svc->hdl)) {
22687697SMichael.Christensen@Sun.COM 		DS_HDL_SET_ISCLIENT(hdl);
22697697SMichael.Christensen@Sun.COM 	}
22707697SMichael.Christensen@Sun.COM 	svc->hdl = hdl;
22717697SMichael.Christensen@Sun.COM 
22727697SMichael.Christensen@Sun.COM 	if (svc->state != DS_SVC_UNREG_PENDING) {
22737697SMichael.Christensen@Sun.COM 		/* try to initiate a new registration */
22747697SMichael.Christensen@Sun.COM 		(void) ds_svc_register(svc, NULL);
22757697SMichael.Christensen@Sun.COM 	}
22767697SMichael.Christensen@Sun.COM 
22777697SMichael.Christensen@Sun.COM 	return (0);
22787697SMichael.Christensen@Sun.COM }
22797697SMichael.Christensen@Sun.COM 
22807697SMichael.Christensen@Sun.COM static int
ds_svc_port_up(ds_svc_t * svc,void * arg)22817697SMichael.Christensen@Sun.COM ds_svc_port_up(ds_svc_t *svc, void *arg)
22827697SMichael.Christensen@Sun.COM {
22837697SMichael.Christensen@Sun.COM 	ds_port_t *port = (ds_port_t *)arg;
22847697SMichael.Christensen@Sun.COM 
22857697SMichael.Christensen@Sun.COM 	if (DS_SVC_ISFREE(svc)) {
22867697SMichael.Christensen@Sun.COM 		/* nothing to do */
22877697SMichael.Christensen@Sun.COM 		return (0);
22887697SMichael.Christensen@Sun.COM 	}
22897697SMichael.Christensen@Sun.COM 
22907697SMichael.Christensen@Sun.COM 	DS_PORTSET_ADD(svc->avail, port->id);
22917697SMichael.Christensen@Sun.COM 	DS_PORTSET_DEL(svc->tried, port->id);
22927697SMichael.Christensen@Sun.COM 
22937697SMichael.Christensen@Sun.COM 	return (0);
22947697SMichael.Christensen@Sun.COM }
22957697SMichael.Christensen@Sun.COM 
229610042SMichael.Christensen@Sun.COM static void
ds_set_port_ready(ds_port_t * port,uint16_t major,uint16_t minor)229710042SMichael.Christensen@Sun.COM ds_set_port_ready(ds_port_t *port, uint16_t major, uint16_t minor)
229810042SMichael.Christensen@Sun.COM {
229910042SMichael.Christensen@Sun.COM 	boolean_t was_ready;
230010042SMichael.Christensen@Sun.COM 
230110042SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
230210042SMichael.Christensen@Sun.COM 	was_ready = (port->state == DS_PORT_READY);
230310042SMichael.Christensen@Sun.COM 	if (!was_ready) {
230410042SMichael.Christensen@Sun.COM 		port->state = DS_PORT_READY;
230510042SMichael.Christensen@Sun.COM 		port->ver.major = major;
230610042SMichael.Christensen@Sun.COM 		port->ver.minor = minor;
230710042SMichael.Christensen@Sun.COM 	}
230810042SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
230910042SMichael.Christensen@Sun.COM 
231010042SMichael.Christensen@Sun.COM 	if (!was_ready) {
231110042SMichael.Christensen@Sun.COM 
231210042SMichael.Christensen@Sun.COM 		/*
231310042SMichael.Christensen@Sun.COM 		 * The port came up, so update all the services
231410042SMichael.Christensen@Sun.COM 		 * with this information. Follow that up with an
231510042SMichael.Christensen@Sun.COM 		 * attempt to register any service that is not
231610042SMichael.Christensen@Sun.COM 		 * already registered.
231710042SMichael.Christensen@Sun.COM 		 */
231810042SMichael.Christensen@Sun.COM 		mutex_enter(&ds_svcs.lock);
231910042SMichael.Christensen@Sun.COM 
232010042SMichael.Christensen@Sun.COM 		(void) ds_walk_svcs(ds_svc_port_up, port);
232110042SMichael.Christensen@Sun.COM 		(void) ds_walk_svcs(ds_svc_register_onport_walker, port);
232210042SMichael.Christensen@Sun.COM 
232310042SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
232410042SMichael.Christensen@Sun.COM 	}
232510042SMichael.Christensen@Sun.COM }
232610042SMichael.Christensen@Sun.COM 
23277697SMichael.Christensen@Sun.COM ds_svc_t *
ds_alloc_svc(void)23287697SMichael.Christensen@Sun.COM ds_alloc_svc(void)
23297697SMichael.Christensen@Sun.COM {
23307697SMichael.Christensen@Sun.COM 	int		idx;
23317697SMichael.Christensen@Sun.COM 	uint_t		newmaxsvcs;
23327697SMichael.Christensen@Sun.COM 	ds_svc_t	**newtbl;
23337697SMichael.Christensen@Sun.COM 	ds_svc_t	*newsvc;
23347697SMichael.Christensen@Sun.COM 
23357697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
23367697SMichael.Christensen@Sun.COM 
23377697SMichael.Christensen@Sun.COM 	idx = ds_walk_svcs(ds_svc_isfree, NULL);
23387697SMichael.Christensen@Sun.COM 
23397697SMichael.Christensen@Sun.COM 	if (idx != ds_svcs.maxsvcs) {
23407697SMichael.Christensen@Sun.COM 		goto found;
23417697SMichael.Christensen@Sun.COM 	}
23427697SMichael.Christensen@Sun.COM 
23437697SMichael.Christensen@Sun.COM 	/*
23447697SMichael.Christensen@Sun.COM 	 * There was no free space in the table. Grow
23457697SMichael.Christensen@Sun.COM 	 * the table to double its current size.
23467697SMichael.Christensen@Sun.COM 	 */
23477697SMichael.Christensen@Sun.COM 	newmaxsvcs = ds_svcs.maxsvcs * 2;
23487697SMichael.Christensen@Sun.COM 	newtbl = DS_MALLOC(newmaxsvcs * sizeof (ds_svc_t *));
23497697SMichael.Christensen@Sun.COM 
23507697SMichael.Christensen@Sun.COM 	/* copy old table data to the new table */
23517697SMichael.Christensen@Sun.COM 	(void) memcpy(newtbl, ds_svcs.tbl,
23527697SMichael.Christensen@Sun.COM 	    ds_svcs.maxsvcs * sizeof (ds_svc_t *));
23537697SMichael.Christensen@Sun.COM 
23547697SMichael.Christensen@Sun.COM 	/* clean up the old table */
23557697SMichael.Christensen@Sun.COM 	DS_FREE(ds_svcs.tbl, ds_svcs.maxsvcs * sizeof (ds_svc_t *));
23567697SMichael.Christensen@Sun.COM 	ds_svcs.tbl = newtbl;
23577697SMichael.Christensen@Sun.COM 	ds_svcs.maxsvcs = newmaxsvcs;
23587697SMichael.Christensen@Sun.COM 
23597697SMichael.Christensen@Sun.COM 	/* search for a free space again */
23607697SMichael.Christensen@Sun.COM 	idx = ds_walk_svcs(ds_svc_isfree, NULL);
23617697SMichael.Christensen@Sun.COM 
23627697SMichael.Christensen@Sun.COM 	/* the table is locked so should find a free slot */
23637697SMichael.Christensen@Sun.COM 	ASSERT(idx != ds_svcs.maxsvcs);
23647697SMichael.Christensen@Sun.COM 
23657697SMichael.Christensen@Sun.COM found:
23667697SMichael.Christensen@Sun.COM 	/* allocate a new svc structure if necessary */
23677697SMichael.Christensen@Sun.COM 	if ((newsvc = ds_svcs.tbl[idx]) == NULL) {
23687697SMichael.Christensen@Sun.COM 		/* allocate a new service */
23697697SMichael.Christensen@Sun.COM 		newsvc = DS_MALLOC(sizeof (ds_svc_t));
23707697SMichael.Christensen@Sun.COM 		ds_svcs.tbl[idx] = newsvc;
23717697SMichael.Christensen@Sun.COM 	}
23727697SMichael.Christensen@Sun.COM 
23737697SMichael.Christensen@Sun.COM 	/* fill in the handle */
23747697SMichael.Christensen@Sun.COM 	newsvc->hdl = DS_ALLOC_HDL(idx, DS_HDL2COUNT(newsvc->hdl));
23757697SMichael.Christensen@Sun.COM 	newsvc->state = DS_SVC_FREE;	/* Mark as free temporarily */
23767697SMichael.Christensen@Sun.COM 
23777697SMichael.Christensen@Sun.COM 	return (newsvc);
23787697SMichael.Christensen@Sun.COM }
23797697SMichael.Christensen@Sun.COM 
23807697SMichael.Christensen@Sun.COM static void
ds_reset_svc(ds_svc_t * svc,ds_port_t * port)23817697SMichael.Christensen@Sun.COM ds_reset_svc(ds_svc_t *svc, ds_port_t *port)
23827697SMichael.Christensen@Sun.COM {
23837697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
23847697SMichael.Christensen@Sun.COM 
23857697SMichael.Christensen@Sun.COM 	if (svc->state != DS_SVC_UNREG_PENDING)
23867697SMichael.Christensen@Sun.COM 		svc->state = DS_SVC_INACTIVE;
23877697SMichael.Christensen@Sun.COM 	svc->ver_idx = 0;
23887697SMichael.Christensen@Sun.COM 	svc->ver.major = 0;
23897697SMichael.Christensen@Sun.COM 	svc->ver.minor = 0;
23907697SMichael.Christensen@Sun.COM 	svc->port = NULL;
23917697SMichael.Christensen@Sun.COM 	if (port) {
23927697SMichael.Christensen@Sun.COM 		DS_PORTSET_DEL(svc->avail, port->id);
23937697SMichael.Christensen@Sun.COM 	}
23947697SMichael.Christensen@Sun.COM }
23957697SMichael.Christensen@Sun.COM 
23967697SMichael.Christensen@Sun.COM ds_svc_t *
ds_get_svc(ds_svc_hdl_t hdl)23977697SMichael.Christensen@Sun.COM ds_get_svc(ds_svc_hdl_t hdl)
23987697SMichael.Christensen@Sun.COM {
23997697SMichael.Christensen@Sun.COM 	int		idx;
24007697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
24017697SMichael.Christensen@Sun.COM 
24027697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
24037697SMichael.Christensen@Sun.COM 
24047697SMichael.Christensen@Sun.COM 	if (hdl == DS_INVALID_HDL)
24057697SMichael.Christensen@Sun.COM 		return (NULL);
24067697SMichael.Christensen@Sun.COM 
24077697SMichael.Christensen@Sun.COM 	idx = DS_HDL2IDX(hdl);
24087697SMichael.Christensen@Sun.COM 
24097697SMichael.Christensen@Sun.COM 	/* check if index is out of bounds */
24107697SMichael.Christensen@Sun.COM 	if ((idx < 0) || (idx >= ds_svcs.maxsvcs))
24117697SMichael.Christensen@Sun.COM 		return (NULL);
24127697SMichael.Christensen@Sun.COM 
24137697SMichael.Christensen@Sun.COM 	svc = ds_svcs.tbl[idx];
24147697SMichael.Christensen@Sun.COM 
24157697SMichael.Christensen@Sun.COM 	/* check for a valid service */
24167697SMichael.Christensen@Sun.COM 	if (DS_SVC_ISFREE(svc))
24177697SMichael.Christensen@Sun.COM 		return (NULL);
24187697SMichael.Christensen@Sun.COM 
24197697SMichael.Christensen@Sun.COM 	/* make sure the handle is an exact match */
24207697SMichael.Christensen@Sun.COM 	if (svc->hdl != hdl)
24217697SMichael.Christensen@Sun.COM 		return (NULL);
24227697SMichael.Christensen@Sun.COM 
24237697SMichael.Christensen@Sun.COM 	return (svc);
24247697SMichael.Christensen@Sun.COM }
24257697SMichael.Christensen@Sun.COM 
24267697SMichael.Christensen@Sun.COM static void
ds_port_reset(ds_port_t * port)24277697SMichael.Christensen@Sun.COM ds_port_reset(ds_port_t *port)
24287697SMichael.Christensen@Sun.COM {
24297697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
24307697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&port->lock));
24317697SMichael.Christensen@Sun.COM 
24327697SMichael.Christensen@Sun.COM 	/* connection went down, mark everything inactive */
24337697SMichael.Christensen@Sun.COM 	(void) ds_walk_svcs(ds_svc_unregister, port);
24347697SMichael.Christensen@Sun.COM 
24357697SMichael.Christensen@Sun.COM 	port->ver_idx = 0;
24367697SMichael.Christensen@Sun.COM 	port->ver.major = 0;
24377697SMichael.Christensen@Sun.COM 	port->ver.minor = 0;
24387697SMichael.Christensen@Sun.COM 	port->state = DS_PORT_LDC_INIT;
24397697SMichael.Christensen@Sun.COM }
24407697SMichael.Christensen@Sun.COM 
24417697SMichael.Christensen@Sun.COM /*
24427697SMichael.Christensen@Sun.COM  * Verify that a version array is sorted as expected for the
24437697SMichael.Christensen@Sun.COM  * version negotiation to work correctly.
24447697SMichael.Christensen@Sun.COM  */
24457697SMichael.Christensen@Sun.COM ds_vers_check_t
ds_vers_isvalid(ds_ver_t * vers,int nvers)24467697SMichael.Christensen@Sun.COM ds_vers_isvalid(ds_ver_t *vers, int nvers)
24477697SMichael.Christensen@Sun.COM {
24487697SMichael.Christensen@Sun.COM 	uint16_t	curr_major;
24497697SMichael.Christensen@Sun.COM 	uint16_t	curr_minor;
24507697SMichael.Christensen@Sun.COM 	int		idx;
24517697SMichael.Christensen@Sun.COM 
24527697SMichael.Christensen@Sun.COM 	curr_major = vers[0].major;
24537697SMichael.Christensen@Sun.COM 	curr_minor = vers[0].minor;
24547697SMichael.Christensen@Sun.COM 
24557697SMichael.Christensen@Sun.COM 	/*
24567697SMichael.Christensen@Sun.COM 	 * Walk the version array, verifying correct ordering.
24577697SMichael.Christensen@Sun.COM 	 * The array must be sorted from highest supported
24587697SMichael.Christensen@Sun.COM 	 * version to lowest supported version.
24597697SMichael.Christensen@Sun.COM 	 */
24607697SMichael.Christensen@Sun.COM 	for (idx = 0; idx < nvers; idx++) {
24617697SMichael.Christensen@Sun.COM 		if (vers[idx].major > curr_major) {
24627697SMichael.Christensen@Sun.COM 			DS_DBG(CE_NOTE, "ds_vers_isvalid: version array has "
24637697SMichael.Christensen@Sun.COM 			    " increasing major versions" DS_EOL);
24647697SMichael.Christensen@Sun.COM 			return (DS_VERS_INCREASING_MAJOR_ERR);
24657697SMichael.Christensen@Sun.COM 		}
24667697SMichael.Christensen@Sun.COM 
24677697SMichael.Christensen@Sun.COM 		if (vers[idx].major < curr_major) {
24687697SMichael.Christensen@Sun.COM 			curr_major = vers[idx].major;
24697697SMichael.Christensen@Sun.COM 			curr_minor = vers[idx].minor;
24707697SMichael.Christensen@Sun.COM 			continue;
24717697SMichael.Christensen@Sun.COM 		}
24727697SMichael.Christensen@Sun.COM 
24737697SMichael.Christensen@Sun.COM 		if (vers[idx].minor > curr_minor) {
24747697SMichael.Christensen@Sun.COM 			DS_DBG(CE_NOTE, "ds_vers_isvalid: version array has "
24757697SMichael.Christensen@Sun.COM 			    " increasing minor versions" DS_EOL);
24767697SMichael.Christensen@Sun.COM 			return (DS_VERS_INCREASING_MINOR_ERR);
24777697SMichael.Christensen@Sun.COM 		}
24787697SMichael.Christensen@Sun.COM 
24797697SMichael.Christensen@Sun.COM 		curr_minor = vers[idx].minor;
24807697SMichael.Christensen@Sun.COM 	}
24817697SMichael.Christensen@Sun.COM 
24827697SMichael.Christensen@Sun.COM 	return (DS_VERS_OK);
24837697SMichael.Christensen@Sun.COM }
24847697SMichael.Christensen@Sun.COM 
24857697SMichael.Christensen@Sun.COM /*
24867697SMichael.Christensen@Sun.COM  * Extended user capability init.
24877697SMichael.Christensen@Sun.COM  */
24887697SMichael.Christensen@Sun.COM int
ds_ucap_init(ds_capability_t * cap,ds_clnt_ops_t * ops,uint32_t flags,int instance,ds_svc_hdl_t * hdlp)24897697SMichael.Christensen@Sun.COM ds_ucap_init(ds_capability_t *cap, ds_clnt_ops_t *ops, uint32_t flags,
24907697SMichael.Christensen@Sun.COM     int instance, ds_svc_hdl_t *hdlp)
24917697SMichael.Christensen@Sun.COM {
24927697SMichael.Christensen@Sun.COM 	ds_vers_check_t	status;
24937697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
24947697SMichael.Christensen@Sun.COM 	int		rv = 0;
24957697SMichael.Christensen@Sun.COM 	ds_svc_hdl_t 	lb_hdl, hdl;
24967697SMichael.Christensen@Sun.COM 	int		is_loopback;
24977697SMichael.Christensen@Sun.COM 	int		is_client;
24987697SMichael.Christensen@Sun.COM 
24997697SMichael.Christensen@Sun.COM 	/* sanity check the args */
25007697SMichael.Christensen@Sun.COM 	if ((cap == NULL) || (ops == NULL)) {
25017697SMichael.Christensen@Sun.COM 		cmn_err(CE_NOTE, "%s: invalid arguments" DS_EOL, __func__);
25027697SMichael.Christensen@Sun.COM 		return (EINVAL);
25037697SMichael.Christensen@Sun.COM 	}
25047697SMichael.Christensen@Sun.COM 
25057697SMichael.Christensen@Sun.COM 	/* sanity check the capability specifier */
25067697SMichael.Christensen@Sun.COM 	if ((cap->svc_id == NULL) || (cap->vers == NULL) || (cap->nvers == 0)) {
25077697SMichael.Christensen@Sun.COM 		cmn_err(CE_NOTE, "%s: invalid capability specifier" DS_EOL,
25087697SMichael.Christensen@Sun.COM 		    __func__);
25097697SMichael.Christensen@Sun.COM 		return (EINVAL);
25107697SMichael.Christensen@Sun.COM 	}
25117697SMichael.Christensen@Sun.COM 
25127697SMichael.Christensen@Sun.COM 	/* sanity check the version array */
25137697SMichael.Christensen@Sun.COM 	if ((status = ds_vers_isvalid(cap->vers, cap->nvers)) != DS_VERS_OK) {
25147697SMichael.Christensen@Sun.COM 		cmn_err(CE_NOTE, "%s: invalid capability version array "
25157697SMichael.Christensen@Sun.COM 		    "for %s service: %s" DS_EOL, __func__, cap->svc_id,
25167697SMichael.Christensen@Sun.COM 		    (status == DS_VERS_INCREASING_MAJOR_ERR) ?
25177697SMichael.Christensen@Sun.COM 		    "increasing major versions" :
25187697SMichael.Christensen@Sun.COM 		    "increasing minor versions");
25197697SMichael.Christensen@Sun.COM 		return (EINVAL);
25207697SMichael.Christensen@Sun.COM 	}
25217697SMichael.Christensen@Sun.COM 
25227697SMichael.Christensen@Sun.COM 	/* data and register callbacks are required */
25237697SMichael.Christensen@Sun.COM 	if ((ops->ds_data_cb == NULL) || (ops->ds_reg_cb == NULL)) {
25247697SMichael.Christensen@Sun.COM 		cmn_err(CE_NOTE, "%s: invalid ops specifier for %s service"
25257697SMichael.Christensen@Sun.COM 		    DS_EOL, __func__, cap->svc_id);
25267697SMichael.Christensen@Sun.COM 		return (EINVAL);
25277697SMichael.Christensen@Sun.COM 	}
25287697SMichael.Christensen@Sun.COM 
25297697SMichael.Christensen@Sun.COM 	flags &= DSSF_USERFLAGS;
25307697SMichael.Christensen@Sun.COM 	is_client = flags & DSSF_ISCLIENT;
25317697SMichael.Christensen@Sun.COM 
25327697SMichael.Christensen@Sun.COM 	DS_DBG_USR(CE_NOTE, "%s: svc_id='%s', data_cb=0x%lx, cb_arg=0x%lx"
25337697SMichael.Christensen@Sun.COM 	    DS_EOL, __func__, cap->svc_id, PTR_TO_LONG(ops->ds_data_cb),
25347697SMichael.Christensen@Sun.COM 	    PTR_TO_LONG(ops->cb_arg));
25357697SMichael.Christensen@Sun.COM 
25367697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
25377697SMichael.Christensen@Sun.COM 
25387697SMichael.Christensen@Sun.COM 	/* check if the service is already registered */
25397697SMichael.Christensen@Sun.COM 	if (i_ds_hdl_lookup(cap->svc_id, is_client, NULL, 1) == 1) {
25407697SMichael.Christensen@Sun.COM 		/* already registered */
2541*12231SMichael.Christensen@Sun.COM 		DS_DBG_USR(CE_NOTE, "Service '%s'/%s already registered" DS_EOL,
25427697SMichael.Christensen@Sun.COM 		    cap->svc_id,
25437697SMichael.Christensen@Sun.COM 		    (flags & DSSF_ISCLIENT) ? "client" : "service");
25447697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
25457697SMichael.Christensen@Sun.COM 		return (EALREADY);
25467697SMichael.Christensen@Sun.COM 	}
25477697SMichael.Christensen@Sun.COM 
25487697SMichael.Christensen@Sun.COM 	svc = ds_alloc_svc();
25497697SMichael.Christensen@Sun.COM 	if (is_client) {
25507697SMichael.Christensen@Sun.COM 		DS_HDL_SET_ISCLIENT(svc->hdl);
25517697SMichael.Christensen@Sun.COM 	}
25527697SMichael.Christensen@Sun.COM 
25537697SMichael.Christensen@Sun.COM 	svc->state = DS_SVC_FREE;
25547697SMichael.Christensen@Sun.COM 	svc->svc_hdl = DS_BADHDL1;
25557697SMichael.Christensen@Sun.COM 
25567697SMichael.Christensen@Sun.COM 	svc->flags = flags;
25577697SMichael.Christensen@Sun.COM 	svc->drvi = instance;
25587697SMichael.Christensen@Sun.COM 	svc->drv_psp = NULL;
25597697SMichael.Christensen@Sun.COM 
25607697SMichael.Christensen@Sun.COM 	/*
25619242SMichael.Christensen@Sun.COM 	 * Check for loopback.  "pri" is a legacy service that assumes it
25629242SMichael.Christensen@Sun.COM 	 * will never use loopback mode.
25637697SMichael.Christensen@Sun.COM 	 */
25649242SMichael.Christensen@Sun.COM 	if (strcmp(cap->svc_id, "pri") == 0) {
25659242SMichael.Christensen@Sun.COM 		is_loopback = 0;
25669242SMichael.Christensen@Sun.COM 	} else if (i_ds_hdl_lookup(cap->svc_id, is_client == 0, &lb_hdl, 1)
25679242SMichael.Christensen@Sun.COM 	    == 1) {
25688172SMichael.Christensen@Sun.COM 		if ((rv = ds_loopback_set_svc(svc, cap, &lb_hdl)) != 0) {
25699242SMichael.Christensen@Sun.COM 			DS_DBG_USR(CE_NOTE, "%s: ds_loopback_set_svc '%s' err "
25709242SMichael.Christensen@Sun.COM 			    " (%d)" DS_EOL, __func__, cap->svc_id, rv);
25717697SMichael.Christensen@Sun.COM 			mutex_exit(&ds_svcs.lock);
25727697SMichael.Christensen@Sun.COM 			return (rv);
25737697SMichael.Christensen@Sun.COM 		}
25747697SMichael.Christensen@Sun.COM 		is_loopback = 1;
25757697SMichael.Christensen@Sun.COM 	} else
25767697SMichael.Christensen@Sun.COM 		is_loopback = 0;
25777697SMichael.Christensen@Sun.COM 
25787697SMichael.Christensen@Sun.COM 	/* copy over all the client information */
25797697SMichael.Christensen@Sun.COM 	(void) memcpy(&svc->cap, cap, sizeof (ds_capability_t));
25807697SMichael.Christensen@Sun.COM 
25817697SMichael.Christensen@Sun.COM 	/* make a copy of the service name */
25827697SMichael.Christensen@Sun.COM 	svc->cap.svc_id = ds_strdup(cap->svc_id);
25837697SMichael.Christensen@Sun.COM 
25847697SMichael.Christensen@Sun.COM 	/* make a copy of the version array */
25857697SMichael.Christensen@Sun.COM 	svc->cap.vers = DS_MALLOC(cap->nvers * sizeof (ds_ver_t));
25867697SMichael.Christensen@Sun.COM 	(void) memcpy(svc->cap.vers, cap->vers, cap->nvers * sizeof (ds_ver_t));
25877697SMichael.Christensen@Sun.COM 
25887697SMichael.Christensen@Sun.COM 	/* copy the client ops vector */
25897697SMichael.Christensen@Sun.COM 	(void) memcpy(&svc->ops, ops, sizeof (ds_clnt_ops_t));
25907697SMichael.Christensen@Sun.COM 
25917697SMichael.Christensen@Sun.COM 	svc->state = DS_SVC_INACTIVE;
25927697SMichael.Christensen@Sun.COM 	svc->ver_idx = 0;
25937697SMichael.Christensen@Sun.COM 	DS_PORTSET_DUP(svc->avail, ds_allports);
25947697SMichael.Christensen@Sun.COM 	DS_PORTSET_SETNULL(svc->tried);
25957697SMichael.Christensen@Sun.COM 
25967697SMichael.Christensen@Sun.COM 	ds_svcs.nsvcs++;
25977697SMichael.Christensen@Sun.COM 
25987697SMichael.Christensen@Sun.COM 	hdl = svc->hdl;
25997697SMichael.Christensen@Sun.COM 
26007697SMichael.Christensen@Sun.COM 	/*
26017697SMichael.Christensen@Sun.COM 	 * kludge to allow user callback code to get handle and user args.
26027697SMichael.Christensen@Sun.COM 	 * Make sure the callback arg points to the svc structure.
26037697SMichael.Christensen@Sun.COM 	 */
26047697SMichael.Christensen@Sun.COM 	if ((flags & DSSF_ISUSER) != 0) {
26057697SMichael.Christensen@Sun.COM 		ds_cbarg_set_cookie(svc);
26067697SMichael.Christensen@Sun.COM 	}
26077697SMichael.Christensen@Sun.COM 
26087697SMichael.Christensen@Sun.COM 	if (is_loopback) {
26097697SMichael.Christensen@Sun.COM 		ds_loopback_register(hdl);
26107697SMichael.Christensen@Sun.COM 		ds_loopback_register(lb_hdl);
26117697SMichael.Christensen@Sun.COM 	}
26127697SMichael.Christensen@Sun.COM 
26137697SMichael.Christensen@Sun.COM 	/*
26147697SMichael.Christensen@Sun.COM 	 * If this is a client or a non-loopback service provider, send
26157697SMichael.Christensen@Sun.COM 	 * out register requests.
26167697SMichael.Christensen@Sun.COM 	 */
26177697SMichael.Christensen@Sun.COM 	if (!is_loopback || (flags & DSSF_ISCLIENT) != 0)
26187697SMichael.Christensen@Sun.COM 		(void) ds_svc_register(svc, NULL);
26197697SMichael.Christensen@Sun.COM 
26207697SMichael.Christensen@Sun.COM 	if (hdlp) {
26217697SMichael.Christensen@Sun.COM 		*hdlp = hdl;
26227697SMichael.Christensen@Sun.COM 	}
26237697SMichael.Christensen@Sun.COM 
26247697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
26257697SMichael.Christensen@Sun.COM 
26267697SMichael.Christensen@Sun.COM 	DS_DBG_USR(CE_NOTE, "%s: service '%s' assigned handle 0x%09lx" DS_EOL,
26277697SMichael.Christensen@Sun.COM 	    __func__, svc->cap.svc_id, hdl);
26287697SMichael.Christensen@Sun.COM 
26297697SMichael.Christensen@Sun.COM 	return (0);
26307697SMichael.Christensen@Sun.COM }
26317697SMichael.Christensen@Sun.COM 
26327697SMichael.Christensen@Sun.COM /*
26337697SMichael.Christensen@Sun.COM  * ds_cap_init interface for previous revision.
26347697SMichael.Christensen@Sun.COM  */
26357697SMichael.Christensen@Sun.COM int
ds_cap_init(ds_capability_t * cap,ds_clnt_ops_t * ops)26367697SMichael.Christensen@Sun.COM ds_cap_init(ds_capability_t *cap, ds_clnt_ops_t *ops)
26377697SMichael.Christensen@Sun.COM {
26387697SMichael.Christensen@Sun.COM 	return (ds_ucap_init(cap, ops, 0, DS_INVALID_INSTANCE, NULL));
26397697SMichael.Christensen@Sun.COM }
26407697SMichael.Christensen@Sun.COM 
26417697SMichael.Christensen@Sun.COM /*
26427697SMichael.Christensen@Sun.COM  * Interface for ds_unreg_hdl in lds driver.
26437697SMichael.Christensen@Sun.COM  */
26447697SMichael.Christensen@Sun.COM int
ds_unreg_hdl(ds_svc_hdl_t hdl)26457697SMichael.Christensen@Sun.COM ds_unreg_hdl(ds_svc_hdl_t hdl)
26467697SMichael.Christensen@Sun.COM {
26477697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
26487697SMichael.Christensen@Sun.COM 	int		is_loopback;
26497697SMichael.Christensen@Sun.COM 	ds_svc_hdl_t	lb_hdl;
26507697SMichael.Christensen@Sun.COM 
26517697SMichael.Christensen@Sun.COM 	DS_DBG_USR(CE_NOTE, "%s: hdl=0x%09lx" DS_EOL, __func__, hdl);
26527697SMichael.Christensen@Sun.COM 
26537697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
26547697SMichael.Christensen@Sun.COM 	if ((svc = ds_get_svc(hdl)) == NULL) {
26557697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
26567697SMichael.Christensen@Sun.COM 		DS_DBG_USR(CE_NOTE, "%s: unknown hdl: 0x%llx" DS_EOL, __func__,
26577697SMichael.Christensen@Sun.COM 		    (u_longlong_t)hdl);
26587697SMichael.Christensen@Sun.COM 		return (ENXIO);
26597697SMichael.Christensen@Sun.COM 	}
26607697SMichael.Christensen@Sun.COM 
26617697SMichael.Christensen@Sun.COM 	DS_DBG_USR(CE_NOTE, "%s: svcid='%s', hdl=0x%llx" DS_EOL, __func__,
26627697SMichael.Christensen@Sun.COM 	    svc->cap.svc_id, (u_longlong_t)svc->hdl);
26637697SMichael.Christensen@Sun.COM 
26647697SMichael.Christensen@Sun.COM 	svc->state = DS_SVC_UNREG_PENDING;
26657697SMichael.Christensen@Sun.COM 
26667697SMichael.Christensen@Sun.COM 	is_loopback = ((svc->flags & DSSF_LOOPBACK) != 0);
26677697SMichael.Christensen@Sun.COM 	lb_hdl = svc->svc_hdl;
26687697SMichael.Christensen@Sun.COM 
26697697SMichael.Christensen@Sun.COM 	if (svc->port) {
26707697SMichael.Christensen@Sun.COM 		(void) ds_send_unreg_req(svc);
26717697SMichael.Christensen@Sun.COM 	}
26727697SMichael.Christensen@Sun.COM 
26737697SMichael.Christensen@Sun.COM 	(void) ds_svc_unregister(svc, svc->port);
26747697SMichael.Christensen@Sun.COM 
26757697SMichael.Christensen@Sun.COM 	ds_delete_svc_entry(svc);
26767697SMichael.Christensen@Sun.COM 
26777697SMichael.Christensen@Sun.COM 	if (is_loopback) {
26787697SMichael.Christensen@Sun.COM 		ds_loopback_unregister(lb_hdl);
26797697SMichael.Christensen@Sun.COM 	}
26807697SMichael.Christensen@Sun.COM 
26817697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
26827697SMichael.Christensen@Sun.COM 
26837697SMichael.Christensen@Sun.COM 	return (0);
26847697SMichael.Christensen@Sun.COM }
26857697SMichael.Christensen@Sun.COM 
26867697SMichael.Christensen@Sun.COM int
ds_cap_fini(ds_capability_t * cap)26877697SMichael.Christensen@Sun.COM ds_cap_fini(ds_capability_t *cap)
26887697SMichael.Christensen@Sun.COM {
26897697SMichael.Christensen@Sun.COM 	ds_svc_hdl_t	hdl;
26907697SMichael.Christensen@Sun.COM 	int rv;
26917697SMichael.Christensen@Sun.COM 	uint_t nhdls = 0;
26927697SMichael.Christensen@Sun.COM 
26937697SMichael.Christensen@Sun.COM 	DS_DBG(CE_NOTE, "%s: '%s'" DS_EOL, __func__, cap->svc_id);
26947697SMichael.Christensen@Sun.COM 	if ((rv = ds_hdl_lookup(cap->svc_id, 0, &hdl, 1, &nhdls)) != 0) {
26957697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "%s: ds_hdl_lookup '%s' err (%d)" DS_EOL,
26967697SMichael.Christensen@Sun.COM 		    __func__, cap->svc_id, rv);
26977697SMichael.Christensen@Sun.COM 		return (rv);
26987697SMichael.Christensen@Sun.COM 	}
26997697SMichael.Christensen@Sun.COM 
27007697SMichael.Christensen@Sun.COM 	if (nhdls == 0) {
27017697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "%s: no such service '%s'" DS_EOL,
27027697SMichael.Christensen@Sun.COM 		    __func__, cap->svc_id);
27037697SMichael.Christensen@Sun.COM 		return (ENXIO);
27047697SMichael.Christensen@Sun.COM 	}
27057697SMichael.Christensen@Sun.COM 
27067697SMichael.Christensen@Sun.COM 	if ((rv = ds_is_my_hdl(hdl, DS_INVALID_INSTANCE)) != 0) {
27077697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "%s: ds_is_my_handle err (%d)" DS_EOL, __func__,
27087697SMichael.Christensen@Sun.COM 		    rv);
27097697SMichael.Christensen@Sun.COM 		return (rv);
27107697SMichael.Christensen@Sun.COM 	}
27117697SMichael.Christensen@Sun.COM 
27127697SMichael.Christensen@Sun.COM 	if ((rv = ds_unreg_hdl(hdl)) != 0) {
27137697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "%s: ds_unreg_hdl err (%d)" DS_EOL, __func__,
27147697SMichael.Christensen@Sun.COM 		    rv);
27157697SMichael.Christensen@Sun.COM 		return (rv);
27167697SMichael.Christensen@Sun.COM 	}
27177697SMichael.Christensen@Sun.COM 
27187697SMichael.Christensen@Sun.COM 	return (0);
27197697SMichael.Christensen@Sun.COM }
27207697SMichael.Christensen@Sun.COM 
27217697SMichael.Christensen@Sun.COM int
ds_cap_send(ds_svc_hdl_t hdl,void * buf,size_t len)27227697SMichael.Christensen@Sun.COM ds_cap_send(ds_svc_hdl_t hdl, void *buf, size_t len)
27237697SMichael.Christensen@Sun.COM {
27247697SMichael.Christensen@Sun.COM 	int		rv;
27257697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
27267697SMichael.Christensen@Sun.COM 	caddr_t		msg;
27277697SMichael.Christensen@Sun.COM 	size_t		msglen;
27287697SMichael.Christensen@Sun.COM 	size_t		hdrlen;
27297697SMichael.Christensen@Sun.COM 	caddr_t		payload;
27307697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
27317697SMichael.Christensen@Sun.COM 	ds_port_t	*port;
27327697SMichael.Christensen@Sun.COM 	ds_data_handle_t *data;
27337697SMichael.Christensen@Sun.COM 	ds_svc_hdl_t	svc_hdl;
27347697SMichael.Christensen@Sun.COM 	int		is_client = 0;
27357697SMichael.Christensen@Sun.COM 
27367697SMichael.Christensen@Sun.COM 	DS_DBG(CE_NOTE, "%s: hdl: 0x%llx, buf: %lx, len: %ld" DS_EOL, __func__,
27377697SMichael.Christensen@Sun.COM 	    (u_longlong_t)hdl, (ulong_t)buf, len);
27387697SMichael.Christensen@Sun.COM 
27397697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
27407697SMichael.Christensen@Sun.COM 
27417697SMichael.Christensen@Sun.COM 	if ((svc = ds_get_svc(hdl)) == NULL) {
27427697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "%s: invalid handle 0x%llx" DS_EOL, __func__,
27437697SMichael.Christensen@Sun.COM 		    (u_longlong_t)hdl);
27447697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
27457697SMichael.Christensen@Sun.COM 		return (ENXIO);
27467697SMichael.Christensen@Sun.COM 	}
27477697SMichael.Christensen@Sun.COM 
27487697SMichael.Christensen@Sun.COM 	if (svc->state != DS_SVC_ACTIVE) {
27497697SMichael.Christensen@Sun.COM 		/* channel is up, but svc is not registered */
27507697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "%s: invalid service state 0x%x" DS_EOL,
27517697SMichael.Christensen@Sun.COM 		    __func__, svc->state);
27527697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
27537697SMichael.Christensen@Sun.COM 		return (ENOTCONN);
27547697SMichael.Christensen@Sun.COM 	}
27557697SMichael.Christensen@Sun.COM 
27567697SMichael.Christensen@Sun.COM 	if (svc->flags & DSSF_LOOPBACK) {
27577697SMichael.Christensen@Sun.COM 		hdl = svc->svc_hdl;
27587697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
27597697SMichael.Christensen@Sun.COM 		ds_loopback_send(hdl, buf, len);
27607697SMichael.Christensen@Sun.COM 		return (0);
27617697SMichael.Christensen@Sun.COM 	}
27627697SMichael.Christensen@Sun.COM 
27637697SMichael.Christensen@Sun.COM 	if ((port = svc->port) == NULL) {
27647697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "%s: service '%s' not associated with a port"
27657697SMichael.Christensen@Sun.COM 		    DS_EOL, __func__, svc->cap.svc_id);
27667697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
27677697SMichael.Christensen@Sun.COM 		return (ECONNRESET);
27687697SMichael.Christensen@Sun.COM 	}
27697697SMichael.Christensen@Sun.COM 
27707697SMichael.Christensen@Sun.COM 	if (svc->flags & DSSF_ISCLIENT) {
27717697SMichael.Christensen@Sun.COM 		is_client = 1;
27727697SMichael.Christensen@Sun.COM 		svc_hdl = svc->svc_hdl;
27737697SMichael.Christensen@Sun.COM 	}
27747697SMichael.Christensen@Sun.COM 
27757697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
27767697SMichael.Christensen@Sun.COM 
27777697SMichael.Christensen@Sun.COM 	/* check that the LDC channel is ready */
27787697SMichael.Christensen@Sun.COM 	if (port->ldc.state != LDC_UP) {
27797697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "%s: LDC channel is not up" DS_EOL, __func__);
27807697SMichael.Christensen@Sun.COM 		return (ECONNRESET);
27817697SMichael.Christensen@Sun.COM 	}
27827697SMichael.Christensen@Sun.COM 
27837697SMichael.Christensen@Sun.COM 	hdrlen = DS_HDR_SZ + sizeof (ds_data_handle_t);
27847697SMichael.Christensen@Sun.COM 
27857697SMichael.Christensen@Sun.COM 	msg = DS_MALLOC(len + hdrlen);
27867697SMichael.Christensen@Sun.COM 	hdr = (ds_hdr_t *)msg;
27877697SMichael.Christensen@Sun.COM 	payload = msg + hdrlen;
27887697SMichael.Christensen@Sun.COM 	msglen = len + hdrlen;
27897697SMichael.Christensen@Sun.COM 
27907697SMichael.Christensen@Sun.COM 	hdr->payload_len = len + sizeof (ds_data_handle_t);
27917697SMichael.Christensen@Sun.COM 	hdr->msg_type = DS_DATA;
27927697SMichael.Christensen@Sun.COM 
27937697SMichael.Christensen@Sun.COM 	data = (ds_data_handle_t *)(msg + DS_HDR_SZ);
27947697SMichael.Christensen@Sun.COM 	if (is_client) {
27957697SMichael.Christensen@Sun.COM 		data->svc_handle = svc_hdl;
27967697SMichael.Christensen@Sun.COM 	} else {
27977697SMichael.Christensen@Sun.COM 		data->svc_handle = hdl;
27987697SMichael.Christensen@Sun.COM 	}
27997697SMichael.Christensen@Sun.COM 
28007697SMichael.Christensen@Sun.COM 	if ((buf != NULL) && (len != 0)) {
28017697SMichael.Christensen@Sun.COM 		(void) memcpy(payload, buf, len);
28027697SMichael.Christensen@Sun.COM 	}
28037697SMichael.Christensen@Sun.COM 
28047697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: data>: hdl=0x%llx, len=%ld, "
28057697SMichael.Christensen@Sun.COM 	    " payload_len=%d" DS_EOL, PORTID(port), (u_longlong_t)svc->hdl,
28067697SMichael.Christensen@Sun.COM 	    msglen, hdr->payload_len);
28077697SMichael.Christensen@Sun.COM 	DS_DUMP_MSG(DS_DBG_FLAG_PRCL, msg, msglen);
28087697SMichael.Christensen@Sun.COM 
28097697SMichael.Christensen@Sun.COM 	if ((rv = ds_send_msg(port, msg, msglen)) != 0) {
28107697SMichael.Christensen@Sun.COM 		rv = (rv == EIO) ? ECONNRESET : rv;
28117697SMichael.Christensen@Sun.COM 	}
28127697SMichael.Christensen@Sun.COM 	DS_FREE(msg, msglen);
28137697SMichael.Christensen@Sun.COM 
28147697SMichael.Christensen@Sun.COM 	return (rv);
28157697SMichael.Christensen@Sun.COM }
28167697SMichael.Christensen@Sun.COM 
28177697SMichael.Christensen@Sun.COM void
ds_port_common_init(ds_port_t * port)28187697SMichael.Christensen@Sun.COM ds_port_common_init(ds_port_t *port)
28197697SMichael.Christensen@Sun.COM {
28207697SMichael.Christensen@Sun.COM 	int rv;
28217697SMichael.Christensen@Sun.COM 
28227697SMichael.Christensen@Sun.COM 	if ((port->flags & DS_PORT_MUTEX_INITED) == 0) {
28237697SMichael.Christensen@Sun.COM 		mutex_init(&port->lock, NULL, MUTEX_DRIVER, NULL);
28247697SMichael.Christensen@Sun.COM 		mutex_init(&port->tx_lock, NULL, MUTEX_DRIVER, NULL);
28257697SMichael.Christensen@Sun.COM 		mutex_init(&port->rcv_lock, NULL, MUTEX_DRIVER, NULL);
28267697SMichael.Christensen@Sun.COM 		port->flags |= DS_PORT_MUTEX_INITED;
28277697SMichael.Christensen@Sun.COM 	}
28287697SMichael.Christensen@Sun.COM 
28297697SMichael.Christensen@Sun.COM 	port->state = DS_PORT_INIT;
28307697SMichael.Christensen@Sun.COM 	DS_PORTSET_ADD(ds_allports, port->id);
28317697SMichael.Christensen@Sun.COM 
28327697SMichael.Christensen@Sun.COM 	ds_sys_port_init(port);
28337697SMichael.Christensen@Sun.COM 
28347697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
28357697SMichael.Christensen@Sun.COM 	rv = ds_ldc_init(port);
28367697SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
28377697SMichael.Christensen@Sun.COM 
28387697SMichael.Christensen@Sun.COM 	/*
28397697SMichael.Christensen@Sun.COM 	 * If LDC successfully init'ed, try to kick off protocol for this port.
28407697SMichael.Christensen@Sun.COM 	 */
28417697SMichael.Christensen@Sun.COM 	if (rv == 0) {
28427697SMichael.Christensen@Sun.COM 		ds_handle_up_event(port);
28437697SMichael.Christensen@Sun.COM 	}
28447697SMichael.Christensen@Sun.COM }
28457697SMichael.Christensen@Sun.COM 
28467697SMichael.Christensen@Sun.COM void
ds_port_common_fini(ds_port_t * port)28479916SMichael.Christensen@Sun.COM ds_port_common_fini(ds_port_t *port)
28487697SMichael.Christensen@Sun.COM {
28499916SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&port->lock));
28509916SMichael.Christensen@Sun.COM 
28517697SMichael.Christensen@Sun.COM 	port->state = DS_PORT_FREE;
28527697SMichael.Christensen@Sun.COM 
28537697SMichael.Christensen@Sun.COM 	DS_PORTSET_DEL(ds_allports, port->id);
28547697SMichael.Christensen@Sun.COM 
28557697SMichael.Christensen@Sun.COM 	ds_sys_port_fini(port);
28567697SMichael.Christensen@Sun.COM }
28577697SMichael.Christensen@Sun.COM 
28587697SMichael.Christensen@Sun.COM /*
28597697SMichael.Christensen@Sun.COM  * Initialize table of registered service classes
28607697SMichael.Christensen@Sun.COM  */
28617697SMichael.Christensen@Sun.COM void
ds_init_svcs_tbl(uint_t nentries)28627697SMichael.Christensen@Sun.COM ds_init_svcs_tbl(uint_t nentries)
28637697SMichael.Christensen@Sun.COM {
28647697SMichael.Christensen@Sun.COM 	int	tblsz;
28657697SMichael.Christensen@Sun.COM 
28667697SMichael.Christensen@Sun.COM 	ds_svcs.maxsvcs = nentries;
28677697SMichael.Christensen@Sun.COM 
28687697SMichael.Christensen@Sun.COM 	tblsz = ds_svcs.maxsvcs * sizeof (ds_svc_t *);
28697697SMichael.Christensen@Sun.COM 	ds_svcs.tbl = (ds_svc_t **)DS_MALLOC(tblsz);
28707697SMichael.Christensen@Sun.COM 
28717697SMichael.Christensen@Sun.COM 	ds_svcs.nsvcs = 0;
28727697SMichael.Christensen@Sun.COM }
28737697SMichael.Christensen@Sun.COM 
28747697SMichael.Christensen@Sun.COM /*
28757697SMichael.Christensen@Sun.COM  * Find the max and min version supported.
28767697SMichael.Christensen@Sun.COM  * Hacked from zeus workspace, support.c
28777697SMichael.Christensen@Sun.COM  */
28787697SMichael.Christensen@Sun.COM static void
min_max_versions(int num_versions,ds_ver_t * sup_versionsp,uint16_t * min_major,uint16_t * max_major)28797697SMichael.Christensen@Sun.COM min_max_versions(int num_versions, ds_ver_t *sup_versionsp,
28807697SMichael.Christensen@Sun.COM     uint16_t *min_major, uint16_t *max_major)
28817697SMichael.Christensen@Sun.COM {
28827697SMichael.Christensen@Sun.COM 	int i;
28837697SMichael.Christensen@Sun.COM 
28847697SMichael.Christensen@Sun.COM 	*min_major = sup_versionsp[0].major;
28857697SMichael.Christensen@Sun.COM 	*max_major = *min_major;
28867697SMichael.Christensen@Sun.COM 
28877697SMichael.Christensen@Sun.COM 	for (i = 1; i < num_versions; i++) {
28887697SMichael.Christensen@Sun.COM 		if (sup_versionsp[i].major < *min_major)
28897697SMichael.Christensen@Sun.COM 			*min_major = sup_versionsp[i].major;
28907697SMichael.Christensen@Sun.COM 
28917697SMichael.Christensen@Sun.COM 		if (sup_versionsp[i].major > *max_major)
28927697SMichael.Christensen@Sun.COM 			*max_major = sup_versionsp[i].major;
28937697SMichael.Christensen@Sun.COM 	}
28947697SMichael.Christensen@Sun.COM }
28957697SMichael.Christensen@Sun.COM 
28967697SMichael.Christensen@Sun.COM /*
28977697SMichael.Christensen@Sun.COM  * Check whether the major and minor numbers requested by the peer can be
28987697SMichael.Christensen@Sun.COM  * satisfied. If the requested major is supported, true is returned, and the
28997697SMichael.Christensen@Sun.COM  * agreed minor is returned in new_minor. If the requested major is not
29007697SMichael.Christensen@Sun.COM  * supported, the routine returns false, and the closest major is returned in
29017697SMichael.Christensen@Sun.COM  * *new_major, upon which the peer should re-negotiate. The closest major is
29027697SMichael.Christensen@Sun.COM  * the just lower that the requested major number.
29037697SMichael.Christensen@Sun.COM  *
29047697SMichael.Christensen@Sun.COM  * Hacked from zeus workspace, support.c
29057697SMichael.Christensen@Sun.COM  */
29067697SMichael.Christensen@Sun.COM boolean_t
negotiate_version(int num_versions,ds_ver_t * sup_versionsp,uint16_t req_major,uint16_t * new_majorp,uint16_t * new_minorp)29077697SMichael.Christensen@Sun.COM negotiate_version(int num_versions, ds_ver_t *sup_versionsp,
29087697SMichael.Christensen@Sun.COM     uint16_t req_major, uint16_t *new_majorp, uint16_t *new_minorp)
29097697SMichael.Christensen@Sun.COM {
29107697SMichael.Christensen@Sun.COM 	int i;
29117697SMichael.Christensen@Sun.COM 	uint16_t major, lower_major;
29127697SMichael.Christensen@Sun.COM 	uint16_t min_major = 0, max_major;
29137697SMichael.Christensen@Sun.COM 	boolean_t found_match = B_FALSE;
29147697SMichael.Christensen@Sun.COM 
29157697SMichael.Christensen@Sun.COM 	min_max_versions(num_versions, sup_versionsp, &min_major, &max_major);
29167697SMichael.Christensen@Sun.COM 
29177697SMichael.Christensen@Sun.COM 	DS_DBG(CE_NOTE, "negotiate_version: req_major = %u, min = %u, max = %u"
29187697SMichael.Christensen@Sun.COM 	    DS_EOL, req_major, min_major, max_major);
29197697SMichael.Christensen@Sun.COM 
29207697SMichael.Christensen@Sun.COM 	/*
29217697SMichael.Christensen@Sun.COM 	 * If the minimum version supported is greater than
29227697SMichael.Christensen@Sun.COM 	 * the version requested, return the lowest version
29237697SMichael.Christensen@Sun.COM 	 * supported
29247697SMichael.Christensen@Sun.COM 	 */
29257697SMichael.Christensen@Sun.COM 	if (min_major > req_major) {
29267697SMichael.Christensen@Sun.COM 		*new_majorp = min_major;
29277697SMichael.Christensen@Sun.COM 		return (B_FALSE);
29287697SMichael.Christensen@Sun.COM 	}
29297697SMichael.Christensen@Sun.COM 
29307697SMichael.Christensen@Sun.COM 	/*
29317697SMichael.Christensen@Sun.COM 	 * If the largest version supported is lower than
29327697SMichael.Christensen@Sun.COM 	 * the version requested, return the largest version
29337697SMichael.Christensen@Sun.COM 	 * supported
29347697SMichael.Christensen@Sun.COM 	 */
29357697SMichael.Christensen@Sun.COM 	if (max_major < req_major) {
29367697SMichael.Christensen@Sun.COM 		*new_majorp = max_major;
29377697SMichael.Christensen@Sun.COM 		return (B_FALSE);
29387697SMichael.Christensen@Sun.COM 	}
29397697SMichael.Christensen@Sun.COM 
29407697SMichael.Christensen@Sun.COM 	/*
29417697SMichael.Christensen@Sun.COM 	 * Now we know that the requested version lies between the
29427697SMichael.Christensen@Sun.COM 	 * min and max versions supported. Check if the requested
29437697SMichael.Christensen@Sun.COM 	 * major can be found in supported versions.
29447697SMichael.Christensen@Sun.COM 	 */
29457697SMichael.Christensen@Sun.COM 	lower_major = min_major;
29467697SMichael.Christensen@Sun.COM 	for (i = 0; i < num_versions; i++) {
29477697SMichael.Christensen@Sun.COM 		major = sup_versionsp[i].major;
29487697SMichael.Christensen@Sun.COM 		if (major == req_major) {
29497697SMichael.Christensen@Sun.COM 			found_match = B_TRUE;
29507697SMichael.Christensen@Sun.COM 			*new_majorp = req_major;
29517697SMichael.Christensen@Sun.COM 			*new_minorp = sup_versionsp[i].minor;
29527697SMichael.Christensen@Sun.COM 			break;
29537697SMichael.Christensen@Sun.COM 		} else {
29547697SMichael.Christensen@Sun.COM 			if ((major < req_major) && (major > lower_major))
29557697SMichael.Christensen@Sun.COM 				lower_major = major;
29567697SMichael.Christensen@Sun.COM 		}
29577697SMichael.Christensen@Sun.COM 	}
29587697SMichael.Christensen@Sun.COM 
29597697SMichael.Christensen@Sun.COM 	/*
29607697SMichael.Christensen@Sun.COM 	 * If no match is found, return the closest available number
29617697SMichael.Christensen@Sun.COM 	 */
29627697SMichael.Christensen@Sun.COM 	if (!found_match)
29637697SMichael.Christensen@Sun.COM 		*new_majorp = lower_major;
29647697SMichael.Christensen@Sun.COM 
29657697SMichael.Christensen@Sun.COM 	return (found_match);
29667697SMichael.Christensen@Sun.COM }
29677697SMichael.Christensen@Sun.COM 
29687697SMichael.Christensen@Sun.COM /*
29697697SMichael.Christensen@Sun.COM  * Specific errno's that are used by ds.c and ldc.c
29707697SMichael.Christensen@Sun.COM  */
29717697SMichael.Christensen@Sun.COM static struct {
29727697SMichael.Christensen@Sun.COM 	int ds_errno;
29737697SMichael.Christensen@Sun.COM 	char *estr;
29747697SMichael.Christensen@Sun.COM } ds_errno_to_str_tab[] = {
29757697SMichael.Christensen@Sun.COM 	{ EIO,		"I/O error" },
29767697SMichael.Christensen@Sun.COM 	{ ENXIO,	"No such device or address" },
29777697SMichael.Christensen@Sun.COM 	{ EAGAIN,	"Resource temporarily unavailable" },
29787697SMichael.Christensen@Sun.COM 	{ ENOMEM,	"Not enough space" },
29797697SMichael.Christensen@Sun.COM 	{ EACCES,	"Permission denied" },
29807697SMichael.Christensen@Sun.COM 	{ EFAULT,	"Bad address" },
29817697SMichael.Christensen@Sun.COM 	{ EBUSY,	"Device busy" },
29827697SMichael.Christensen@Sun.COM 	{ EINVAL,	"Invalid argument" },
29837697SMichael.Christensen@Sun.COM 	{ ENOSPC,	"No space left on device" },
29847697SMichael.Christensen@Sun.COM 	{ ENOMSG,	"No message of desired type" },
29857697SMichael.Christensen@Sun.COM #ifdef	ECHRNG
29867697SMichael.Christensen@Sun.COM 	{ ECHRNG,	"Channel number out of range" },
29877697SMichael.Christensen@Sun.COM #endif
29887697SMichael.Christensen@Sun.COM 	{ ENOTSUP,	"Operation not supported" },
29897697SMichael.Christensen@Sun.COM 	{ EMSGSIZE,	"Message too long" },
29907697SMichael.Christensen@Sun.COM 	{ EADDRINUSE,	"Address already in use" },
29917697SMichael.Christensen@Sun.COM 	{ ECONNRESET,	"Connection reset by peer" },
29927697SMichael.Christensen@Sun.COM 	{ ENOBUFS,	"No buffer space available" },
29937697SMichael.Christensen@Sun.COM 	{ ENOTCONN,	"Socket is not connected" },
29947697SMichael.Christensen@Sun.COM 	{ ECONNREFUSED,	"Connection refused" },
29957697SMichael.Christensen@Sun.COM 	{ EALREADY,	"Operation already in progress" },
29967697SMichael.Christensen@Sun.COM 	{ 0,		NULL },
29977697SMichael.Christensen@Sun.COM };
29987697SMichael.Christensen@Sun.COM 
29997697SMichael.Christensen@Sun.COM char *
ds_errno_to_str(int ds_errno,char * ebuf)30007697SMichael.Christensen@Sun.COM ds_errno_to_str(int ds_errno, char *ebuf)
30017697SMichael.Christensen@Sun.COM {
30027697SMichael.Christensen@Sun.COM 	int i, en;
30037697SMichael.Christensen@Sun.COM 
30047697SMichael.Christensen@Sun.COM 	for (i = 0; (en = ds_errno_to_str_tab[i].ds_errno) != 0; i++) {
30057697SMichael.Christensen@Sun.COM 		if (en == ds_errno) {
30067697SMichael.Christensen@Sun.COM 			(void) strcpy(ebuf, ds_errno_to_str_tab[i].estr);
30077697SMichael.Christensen@Sun.COM 			return (ebuf);
30087697SMichael.Christensen@Sun.COM 		}
30097697SMichael.Christensen@Sun.COM 	}
30107697SMichael.Christensen@Sun.COM 
30117697SMichael.Christensen@Sun.COM 	(void) sprintf(ebuf, "ds_errno (%d)", ds_errno);
30127697SMichael.Christensen@Sun.COM 	return (ebuf);
30137697SMichael.Christensen@Sun.COM }
30147697SMichael.Christensen@Sun.COM 
30157697SMichael.Christensen@Sun.COM static void
ds_loopback_register(ds_svc_hdl_t hdl)30167697SMichael.Christensen@Sun.COM ds_loopback_register(ds_svc_hdl_t hdl)
30177697SMichael.Christensen@Sun.COM {
30188172SMichael.Christensen@Sun.COM 	ds_ver_t ds_ver;
30197697SMichael.Christensen@Sun.COM 	ds_svc_t *svc;
30207697SMichael.Christensen@Sun.COM 
30217697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
30227697SMichael.Christensen@Sun.COM 	DS_DBG_LOOP(CE_NOTE, "%s: entered hdl: 0x%llx" DS_EOL, __func__,
30237697SMichael.Christensen@Sun.COM 	    (u_longlong_t)hdl);
30247697SMichael.Christensen@Sun.COM 	if ((svc = ds_get_svc(hdl)) == NULL) {
30257697SMichael.Christensen@Sun.COM 		DS_DBG_LOOP(CE_NOTE, "%s: invalid hdl: 0x%llx" DS_EOL, __func__,
30267697SMichael.Christensen@Sun.COM 		    (u_longlong_t)hdl);
30277697SMichael.Christensen@Sun.COM 		return;
30287697SMichael.Christensen@Sun.COM 	}
30298172SMichael.Christensen@Sun.COM 
30307697SMichael.Christensen@Sun.COM 	svc->state = DS_SVC_ACTIVE;
30317697SMichael.Christensen@Sun.COM 
30327697SMichael.Christensen@Sun.COM 	if (svc->ops.ds_reg_cb) {
30337697SMichael.Christensen@Sun.COM 		DS_DBG_LOOP(CE_NOTE, "%s: loopback regcb: hdl: 0x%llx" DS_EOL,
30347697SMichael.Christensen@Sun.COM 		    __func__, (u_longlong_t)hdl);
30357697SMichael.Christensen@Sun.COM 		ds_ver.major = svc->ver.major;
30367697SMichael.Christensen@Sun.COM 		ds_ver.minor = svc->ver.minor;
30377697SMichael.Christensen@Sun.COM 		(*svc->ops.ds_reg_cb)(svc->ops.cb_arg, &ds_ver, hdl);
30387697SMichael.Christensen@Sun.COM 	}
30397697SMichael.Christensen@Sun.COM }
30407697SMichael.Christensen@Sun.COM 
30417697SMichael.Christensen@Sun.COM static void
ds_loopback_unregister(ds_svc_hdl_t hdl)30427697SMichael.Christensen@Sun.COM ds_loopback_unregister(ds_svc_hdl_t hdl)
30437697SMichael.Christensen@Sun.COM {
30447697SMichael.Christensen@Sun.COM 	ds_svc_t *svc;
30457697SMichael.Christensen@Sun.COM 
30467697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
30477697SMichael.Christensen@Sun.COM 	if ((svc = ds_get_svc(hdl)) == NULL) {
30487697SMichael.Christensen@Sun.COM 		DS_DBG_LOOP(CE_NOTE, "%s: invalid hdl: 0x%llx" DS_EOL, __func__,
30497697SMichael.Christensen@Sun.COM 		    (u_longlong_t)hdl);
30507697SMichael.Christensen@Sun.COM 		return;
30517697SMichael.Christensen@Sun.COM 	}
30527697SMichael.Christensen@Sun.COM 
30537697SMichael.Christensen@Sun.COM 	DS_DBG_LOOP(CE_NOTE, "%s: entered hdl: 0x%llx" DS_EOL, __func__,
30547697SMichael.Christensen@Sun.COM 	    (u_longlong_t)hdl);
30557697SMichael.Christensen@Sun.COM 
30567697SMichael.Christensen@Sun.COM 	svc->flags &= ~DSSF_LOOPBACK;
30577697SMichael.Christensen@Sun.COM 	svc->svc_hdl = DS_BADHDL2;
30588172SMichael.Christensen@Sun.COM 	svc->state = DS_SVC_INACTIVE;
30597697SMichael.Christensen@Sun.COM 
30607697SMichael.Christensen@Sun.COM 	if (svc->ops.ds_unreg_cb) {
30617697SMichael.Christensen@Sun.COM 		DS_DBG_LOOP(CE_NOTE, "%s: loopback unregcb: hdl: 0x%llx" DS_EOL,
30627697SMichael.Christensen@Sun.COM 		    __func__, (u_longlong_t)hdl);
30637697SMichael.Christensen@Sun.COM 		(*svc->ops.ds_unreg_cb)(svc->ops.cb_arg);
30647697SMichael.Christensen@Sun.COM 	}
30657697SMichael.Christensen@Sun.COM }
30667697SMichael.Christensen@Sun.COM 
30677697SMichael.Christensen@Sun.COM static void
ds_loopback_send(ds_svc_hdl_t hdl,void * buf,size_t buflen)30687697SMichael.Christensen@Sun.COM ds_loopback_send(ds_svc_hdl_t hdl, void *buf, size_t buflen)
30697697SMichael.Christensen@Sun.COM {
30707697SMichael.Christensen@Sun.COM 	ds_svc_t *svc;
30717697SMichael.Christensen@Sun.COM 
30727697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
30737697SMichael.Christensen@Sun.COM 	if ((svc = ds_get_svc(hdl)) == NULL) {
30747697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
30757697SMichael.Christensen@Sun.COM 		DS_DBG_LOOP(CE_NOTE, "%s: invalid hdl: 0x%llx" DS_EOL, __func__,
30767697SMichael.Christensen@Sun.COM 		    (u_longlong_t)hdl);
30777697SMichael.Christensen@Sun.COM 		return;
30787697SMichael.Christensen@Sun.COM 	}
30797697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
30807697SMichael.Christensen@Sun.COM 
30817697SMichael.Christensen@Sun.COM 	DS_DBG_LOOP(CE_NOTE, "%s: entered hdl: 0x%llx" DS_EOL, __func__,
30827697SMichael.Christensen@Sun.COM 	    (u_longlong_t)hdl);
30837697SMichael.Christensen@Sun.COM 
30847697SMichael.Christensen@Sun.COM 	if (svc->ops.ds_data_cb) {
30857697SMichael.Christensen@Sun.COM 		DS_DBG_LOOP(CE_NOTE, "%s: loopback datacb hdl: 0x%llx" DS_EOL,
30867697SMichael.Christensen@Sun.COM 		    __func__, (u_longlong_t)hdl);
30877697SMichael.Christensen@Sun.COM 		(*svc->ops.ds_data_cb)(svc->ops.cb_arg, buf, buflen);
30887697SMichael.Christensen@Sun.COM 	}
30897697SMichael.Christensen@Sun.COM }
30907697SMichael.Christensen@Sun.COM 
30917697SMichael.Christensen@Sun.COM static int
ds_loopback_set_svc(ds_svc_t * svc,ds_capability_t * cap,ds_svc_hdl_t * lb_hdlp)30928172SMichael.Christensen@Sun.COM ds_loopback_set_svc(ds_svc_t *svc, ds_capability_t *cap, ds_svc_hdl_t *lb_hdlp)
30937697SMichael.Christensen@Sun.COM {
30947697SMichael.Christensen@Sun.COM 	ds_svc_t *lb_svc;
30958172SMichael.Christensen@Sun.COM 	ds_svc_hdl_t lb_hdl = *lb_hdlp;
30968172SMichael.Christensen@Sun.COM 	int i;
30978172SMichael.Christensen@Sun.COM 	int match = 0;
30988172SMichael.Christensen@Sun.COM 	uint16_t new_major;
30998172SMichael.Christensen@Sun.COM 	uint16_t new_minor;
31007697SMichael.Christensen@Sun.COM 
31017697SMichael.Christensen@Sun.COM 	if ((lb_svc = ds_get_svc(lb_hdl)) == NULL) {
31027697SMichael.Christensen@Sun.COM 		DS_DBG_LOOP(CE_NOTE, "%s: loopback: hdl: 0x%llx invalid" DS_EOL,
31037697SMichael.Christensen@Sun.COM 		    __func__, (u_longlong_t)lb_hdl);
31047697SMichael.Christensen@Sun.COM 		return (ENXIO);
31057697SMichael.Christensen@Sun.COM 	}
31068172SMichael.Christensen@Sun.COM 
31078172SMichael.Christensen@Sun.COM 	/* negotiate a version between loopback services, if possible */
31088172SMichael.Christensen@Sun.COM 	for (i = 0; i < lb_svc->cap.nvers && match == 0; i++) {
31098172SMichael.Christensen@Sun.COM 		match = negotiate_version(cap->nvers, cap->vers,
31108172SMichael.Christensen@Sun.COM 		    lb_svc->cap.vers[i].major, &new_major, &new_minor);
31118172SMichael.Christensen@Sun.COM 	}
31128172SMichael.Christensen@Sun.COM 	if (!match) {
31138172SMichael.Christensen@Sun.COM 		DS_DBG_LOOP(CE_NOTE, "%s: loopback version negotiate failed"
31148172SMichael.Christensen@Sun.COM 		    DS_EOL, __func__);
31158172SMichael.Christensen@Sun.COM 		return (ENOTSUP);
31168172SMichael.Christensen@Sun.COM 	}
311711669SMichael.Christensen@Sun.COM 
311811669SMichael.Christensen@Sun.COM 	/*
311911669SMichael.Christensen@Sun.COM 	 * If a client service is not inactive, clone it.  If the service is
312011669SMichael.Christensen@Sun.COM 	 * not a client service and has a reg req pending (usually from OBP
312111669SMichael.Christensen@Sun.COM 	 * in boot state not acking/nacking reg req's), it's OK to ignore that,
312211669SMichael.Christensen@Sun.COM 	 * since there are never multiple service clients.  Also reg req pending
312311669SMichael.Christensen@Sun.COM 	 * only happens for non-client services, so it's OK to skip
312411669SMichael.Christensen@Sun.COM 	 * this block that does client service cloning.
312511669SMichael.Christensen@Sun.COM 	 */
312611669SMichael.Christensen@Sun.COM 	if (lb_svc->state != DS_SVC_INACTIVE &&
312711669SMichael.Christensen@Sun.COM 	    lb_svc->state != DS_SVC_REG_PENDING) {
31288172SMichael.Christensen@Sun.COM 		DS_DBG_LOOP(CE_NOTE, "%s: loopback active: hdl: 0x%llx"
31297697SMichael.Christensen@Sun.COM 		    DS_EOL, __func__, (u_longlong_t)lb_hdl);
31307697SMichael.Christensen@Sun.COM 		if ((lb_svc->flags & DSSF_ISCLIENT) == 0) {
31317697SMichael.Christensen@Sun.COM 			DS_DBG_LOOP(CE_NOTE, "%s: loopback busy hdl: 0x%llx"
31327697SMichael.Christensen@Sun.COM 			    DS_EOL, __func__, (u_longlong_t)lb_hdl);
31337697SMichael.Christensen@Sun.COM 			return (EBUSY);
31347697SMichael.Christensen@Sun.COM 		}
31358172SMichael.Christensen@Sun.COM 		svc->state = DS_SVC_INACTIVE;	/* prevent alloc'ing svc */
31367697SMichael.Christensen@Sun.COM 		lb_svc = ds_svc_clone(lb_svc);
31377697SMichael.Christensen@Sun.COM 		DS_DBG_LOOP(CE_NOTE, "%s: loopback clone: ohdl: 0x%llx "
31387697SMichael.Christensen@Sun.COM 		    "nhdl: 0x%llx" DS_EOL, __func__, (u_longlong_t)lb_hdl,
31397697SMichael.Christensen@Sun.COM 		    (u_longlong_t)lb_svc->hdl);
31408172SMichael.Christensen@Sun.COM 		*lb_hdlp = lb_svc->hdl;
31417697SMichael.Christensen@Sun.COM 	}
31427697SMichael.Christensen@Sun.COM 
31437697SMichael.Christensen@Sun.COM 	svc->flags |= DSSF_LOOPBACK;
31447697SMichael.Christensen@Sun.COM 	svc->svc_hdl = lb_svc->hdl;
31457697SMichael.Christensen@Sun.COM 	svc->port = NULL;
31468172SMichael.Christensen@Sun.COM 	svc->ver.major = new_major;
31478172SMichael.Christensen@Sun.COM 	svc->ver.minor = new_minor;
31487697SMichael.Christensen@Sun.COM 
31497697SMichael.Christensen@Sun.COM 	lb_svc->flags |= DSSF_LOOPBACK;
31507697SMichael.Christensen@Sun.COM 	lb_svc->svc_hdl = svc->hdl;
31517697SMichael.Christensen@Sun.COM 	lb_svc->port = NULL;
31528172SMichael.Christensen@Sun.COM 	lb_svc->ver.major = new_major;
31538172SMichael.Christensen@Sun.COM 	lb_svc->ver.minor = new_minor;
31547697SMichael.Christensen@Sun.COM 
31557697SMichael.Christensen@Sun.COM 	DS_DBG_LOOP(CE_NOTE, "%s: setting loopback between: 0x%llx and 0x%llx"
31567697SMichael.Christensen@Sun.COM 	    DS_EOL, __func__, (u_longlong_t)svc->hdl,
31577697SMichael.Christensen@Sun.COM 	    (u_longlong_t)lb_svc->hdl);
31587697SMichael.Christensen@Sun.COM 	return (0);
31597697SMichael.Christensen@Sun.COM }
31607697SMichael.Christensen@Sun.COM 
31617697SMichael.Christensen@Sun.COM static ds_svc_t *
ds_find_clnt_svc_by_hdl_port(ds_svc_hdl_t hdl,ds_port_t * port)31627697SMichael.Christensen@Sun.COM ds_find_clnt_svc_by_hdl_port(ds_svc_hdl_t hdl, ds_port_t *port)
31637697SMichael.Christensen@Sun.COM {
31647697SMichael.Christensen@Sun.COM 	int		idx;
31657697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
31667697SMichael.Christensen@Sun.COM 
31677697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: %s looking up clnt hdl: 0x%llx" DS_EOL,
31687697SMichael.Christensen@Sun.COM 	    PORTID(port), __func__, (u_longlong_t)hdl);
31697697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
31707697SMichael.Christensen@Sun.COM 
31717697SMichael.Christensen@Sun.COM 	/* walk every table entry */
31727697SMichael.Christensen@Sun.COM 	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
31737697SMichael.Christensen@Sun.COM 		svc = ds_svcs.tbl[idx];
31747697SMichael.Christensen@Sun.COM 		if (DS_SVC_ISFREE(svc))
31757697SMichael.Christensen@Sun.COM 			continue;
31767697SMichael.Christensen@Sun.COM 		if ((svc->flags & DSSF_ISCLIENT) != 0 &&
31777697SMichael.Christensen@Sun.COM 		    svc->svc_hdl == hdl && svc->port == port) {
31787697SMichael.Christensen@Sun.COM 			DS_DBG_PRCL(CE_NOTE, "ds@%lx: %s found clnt hdl "
31797697SMichael.Christensen@Sun.COM 			    "0x%llx: svc%d" DS_EOL, PORTID(port), __func__,
31807697SMichael.Christensen@Sun.COM 			    (u_longlong_t)hdl, (uint_t)DS_HDL2IDX(svc->hdl));
31817697SMichael.Christensen@Sun.COM 			return (svc);
31827697SMichael.Christensen@Sun.COM 		}
31837697SMichael.Christensen@Sun.COM 	}
31847697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: %s clnt hdl: 0x%llx not found" DS_EOL,
31857697SMichael.Christensen@Sun.COM 	    PORTID(port), __func__, (u_longlong_t)hdl);
31867697SMichael.Christensen@Sun.COM 
31877697SMichael.Christensen@Sun.COM 	return (NULL);
31887697SMichael.Christensen@Sun.COM }
31897697SMichael.Christensen@Sun.COM 
31907697SMichael.Christensen@Sun.COM static ds_svc_t *
ds_svc_clone(ds_svc_t * svc)31917697SMichael.Christensen@Sun.COM ds_svc_clone(ds_svc_t *svc)
31927697SMichael.Christensen@Sun.COM {
31937697SMichael.Christensen@Sun.COM 	ds_svc_t *newsvc;
31947697SMichael.Christensen@Sun.COM 	ds_svc_hdl_t hdl;
31957697SMichael.Christensen@Sun.COM 
31967697SMichael.Christensen@Sun.COM 	ASSERT(svc->flags & DSSF_ISCLIENT);
31977697SMichael.Christensen@Sun.COM 
31987697SMichael.Christensen@Sun.COM 	newsvc = ds_alloc_svc();
31997697SMichael.Christensen@Sun.COM 
32007697SMichael.Christensen@Sun.COM 	/* Can only clone clients for now */
32017697SMichael.Christensen@Sun.COM 	hdl = newsvc->hdl | DS_HDL_ISCLIENT_BIT;
32027697SMichael.Christensen@Sun.COM 	DS_DBG_USR(CE_NOTE, "%s: cloning client: old hdl: 0x%llx new hdl: "
32037697SMichael.Christensen@Sun.COM 	    "0x%llx" DS_EOL, __func__, (u_longlong_t)svc->hdl,
32047697SMichael.Christensen@Sun.COM 	    (u_longlong_t)hdl);
32057697SMichael.Christensen@Sun.COM 	(void) memcpy(newsvc, svc, sizeof (ds_svc_t));
32067697SMichael.Christensen@Sun.COM 	newsvc->hdl = hdl;
32077697SMichael.Christensen@Sun.COM 	newsvc->flags &= ~DSSF_LOOPBACK;
32087697SMichael.Christensen@Sun.COM 	newsvc->port = NULL;
32097697SMichael.Christensen@Sun.COM 	newsvc->svc_hdl = DS_BADHDL2;
32107697SMichael.Christensen@Sun.COM 	newsvc->cap.svc_id = ds_strdup(svc->cap.svc_id);
32117697SMichael.Christensen@Sun.COM 	newsvc->cap.vers = DS_MALLOC(svc->cap.nvers * sizeof (ds_ver_t));
32127697SMichael.Christensen@Sun.COM 	(void) memcpy(newsvc->cap.vers, svc->cap.vers,
32137697SMichael.Christensen@Sun.COM 	    svc->cap.nvers * sizeof (ds_ver_t));
32147697SMichael.Christensen@Sun.COM 
32157697SMichael.Christensen@Sun.COM 	/*
32167697SMichael.Christensen@Sun.COM 	 * Kludge to allow lds driver user callbacks to get access to current
32177697SMichael.Christensen@Sun.COM 	 * svc structure.  Arg could be index to svc table or some other piece
32187697SMichael.Christensen@Sun.COM 	 * of info to get to the svc table entry.
32197697SMichael.Christensen@Sun.COM 	 */
32207697SMichael.Christensen@Sun.COM 	if (newsvc->flags & DSSF_ISUSER) {
32217697SMichael.Christensen@Sun.COM 		newsvc->ops.cb_arg = (ds_cb_arg_t)(newsvc);
32227697SMichael.Christensen@Sun.COM 	}
32237697SMichael.Christensen@Sun.COM 	return (newsvc);
32247697SMichael.Christensen@Sun.COM }
32257697SMichael.Christensen@Sun.COM 
32267697SMichael.Christensen@Sun.COM /*
32277697SMichael.Christensen@Sun.COM  * Internal handle lookup function.
32287697SMichael.Christensen@Sun.COM  */
32297697SMichael.Christensen@Sun.COM static int
i_ds_hdl_lookup(char * service,uint_t is_client,ds_svc_hdl_t * hdlp,uint_t maxhdls)32307697SMichael.Christensen@Sun.COM i_ds_hdl_lookup(char *service, uint_t is_client, ds_svc_hdl_t *hdlp,
32317697SMichael.Christensen@Sun.COM     uint_t maxhdls)
32327697SMichael.Christensen@Sun.COM {
32337697SMichael.Christensen@Sun.COM 	int idx;
32347697SMichael.Christensen@Sun.COM 	int nhdls = 0;
32357697SMichael.Christensen@Sun.COM 	ds_svc_t *svc;
32367697SMichael.Christensen@Sun.COM 	uint32_t client_flag = is_client ? DSSF_ISCLIENT : 0;
32377697SMichael.Christensen@Sun.COM 
32387697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
32397697SMichael.Christensen@Sun.COM 
32407697SMichael.Christensen@Sun.COM 	for (idx = 0; idx < ds_svcs.maxsvcs && nhdls < maxhdls; idx++) {
32417697SMichael.Christensen@Sun.COM 		svc = ds_svcs.tbl[idx];
32427697SMichael.Christensen@Sun.COM 		if (DS_SVC_ISFREE(svc))
32437697SMichael.Christensen@Sun.COM 			continue;
32447697SMichael.Christensen@Sun.COM 		if (strcmp(svc->cap.svc_id, service) == 0 &&
32457697SMichael.Christensen@Sun.COM 		    (svc->flags & DSSF_ISCLIENT) == client_flag) {
32467697SMichael.Christensen@Sun.COM 			if (hdlp != NULL && nhdls < maxhdls) {
32477697SMichael.Christensen@Sun.COM 				hdlp[nhdls] = svc->hdl;
32487697SMichael.Christensen@Sun.COM 				nhdls++;
32497697SMichael.Christensen@Sun.COM 			} else {
32507697SMichael.Christensen@Sun.COM 				nhdls++;
32517697SMichael.Christensen@Sun.COM 			}
32527697SMichael.Christensen@Sun.COM 		}
32537697SMichael.Christensen@Sun.COM 	}
32547697SMichael.Christensen@Sun.COM 	return (nhdls);
32557697SMichael.Christensen@Sun.COM }
32567697SMichael.Christensen@Sun.COM 
32577697SMichael.Christensen@Sun.COM /*
32587697SMichael.Christensen@Sun.COM  * Interface for ds_hdl_lookup in lds driver.
32597697SMichael.Christensen@Sun.COM  */
32607697SMichael.Christensen@Sun.COM int
ds_hdl_lookup(char * service,uint_t is_client,ds_svc_hdl_t * hdlp,uint_t maxhdls,uint_t * nhdlsp)32617697SMichael.Christensen@Sun.COM ds_hdl_lookup(char *service, uint_t is_client, ds_svc_hdl_t *hdlp,
32627697SMichael.Christensen@Sun.COM     uint_t maxhdls, uint_t *nhdlsp)
32637697SMichael.Christensen@Sun.COM {
32647697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
32657697SMichael.Christensen@Sun.COM 	*nhdlsp = i_ds_hdl_lookup(service, is_client, hdlp, maxhdls);
32667697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
32677697SMichael.Christensen@Sun.COM 	return (0);
32687697SMichael.Christensen@Sun.COM }
32697697SMichael.Christensen@Sun.COM 
32707697SMichael.Christensen@Sun.COM /*
32717697SMichael.Christensen@Sun.COM  * After an UNREG REQ, check if this is a client service with multiple
32727697SMichael.Christensen@Sun.COM  * handles.  If it is, then we can eliminate this entry.
32737697SMichael.Christensen@Sun.COM  */
32747697SMichael.Christensen@Sun.COM static void
ds_check_for_dup_services(ds_svc_t * svc)32757697SMichael.Christensen@Sun.COM ds_check_for_dup_services(ds_svc_t *svc)
32767697SMichael.Christensen@Sun.COM {
32777697SMichael.Christensen@Sun.COM 	if ((svc->flags & DSSF_ISCLIENT) != 0 &&
32787697SMichael.Christensen@Sun.COM 	    svc->state == DS_SVC_INACTIVE &&
32797697SMichael.Christensen@Sun.COM 	    i_ds_hdl_lookup(svc->cap.svc_id, 1, NULL, 2) == 2) {
32807697SMichael.Christensen@Sun.COM 		ds_delete_svc_entry(svc);
32817697SMichael.Christensen@Sun.COM 	}
32827697SMichael.Christensen@Sun.COM }
32837697SMichael.Christensen@Sun.COM 
32847697SMichael.Christensen@Sun.COM static void
ds_delete_svc_entry(ds_svc_t * svc)32857697SMichael.Christensen@Sun.COM ds_delete_svc_entry(ds_svc_t *svc)
32867697SMichael.Christensen@Sun.COM {
32877697SMichael.Christensen@Sun.COM 	ds_svc_hdl_t tmp_hdl;
32887697SMichael.Christensen@Sun.COM 
32897697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
32907697SMichael.Christensen@Sun.COM 
32917697SMichael.Christensen@Sun.COM 	/*
32927697SMichael.Christensen@Sun.COM 	 * Clear out the structure, but do not deallocate the
32937697SMichael.Christensen@Sun.COM 	 * memory. It can be reused for the next registration.
32947697SMichael.Christensen@Sun.COM 	 */
32957697SMichael.Christensen@Sun.COM 	DS_FREE(svc->cap.svc_id, strlen(svc->cap.svc_id) + 1);
32967697SMichael.Christensen@Sun.COM 	DS_FREE(svc->cap.vers, svc->cap.nvers * sizeof (ds_ver_t));
32977697SMichael.Christensen@Sun.COM 
32987697SMichael.Christensen@Sun.COM 	/* save the handle to prevent reuse */
32997697SMichael.Christensen@Sun.COM 	tmp_hdl = svc->hdl;
33007697SMichael.Christensen@Sun.COM 	bzero((void *)svc, sizeof (ds_svc_t));
33017697SMichael.Christensen@Sun.COM 
33027697SMichael.Christensen@Sun.COM 	/* initialize for next use */
33037697SMichael.Christensen@Sun.COM 	svc->hdl = tmp_hdl;
33047697SMichael.Christensen@Sun.COM 	svc->state = DS_SVC_FREE;
33057697SMichael.Christensen@Sun.COM 
33067697SMichael.Christensen@Sun.COM 	ds_svcs.nsvcs--;
33077697SMichael.Christensen@Sun.COM }
3308