xref: /illumos-gate/usr/src/uts/sun4v/io/ds_common.c (revision cb453c7a)
130588217SMike Christensen /*
230588217SMike Christensen  * CDDL HEADER START
330588217SMike Christensen  *
430588217SMike Christensen  * The contents of this file are subject to the terms of the
530588217SMike Christensen  * Common Development and Distribution License (the "License").
630588217SMike Christensen  * You may not use this file except in compliance with the License.
730588217SMike Christensen  *
830588217SMike Christensen  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
930588217SMike Christensen  * or http://www.opensolaris.org/os/licensing.
1030588217SMike Christensen  * See the License for the specific language governing permissions
1130588217SMike Christensen  * and limitations under the License.
1230588217SMike Christensen  *
1330588217SMike Christensen  * When distributing Covered Code, include this CDDL HEADER in each
1430588217SMike Christensen  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1530588217SMike Christensen  * If applicable, add the following below this CDDL HEADER, with the
1630588217SMike Christensen  * fields enclosed by brackets "[]" replaced with your own identifying
1730588217SMike Christensen  * information: Portions Copyright [yyyy] [name of copyright owner]
1830588217SMike Christensen  *
1930588217SMike Christensen  * CDDL HEADER END
2030588217SMike Christensen  */
2130588217SMike Christensen 
2230588217SMike Christensen /*
23e9406929SMike Christensen  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
2430588217SMike Christensen  */
2530588217SMike Christensen 
2630588217SMike Christensen /*
2730588217SMike Christensen  * Domain Services Module Common Code.
2830588217SMike Christensen  *
2930588217SMike Christensen  * This module is intended to be used by both Solaris and the VBSC
3030588217SMike Christensen  * module.
3130588217SMike Christensen  */
3230588217SMike Christensen 
3330588217SMike Christensen #include <sys/modctl.h>
3430588217SMike Christensen #include <sys/ksynch.h>
3530588217SMike Christensen #include <sys/taskq.h>
3630588217SMike Christensen #include <sys/disp.h>
3730588217SMike Christensen #include <sys/cmn_err.h>
3830588217SMike Christensen #include <sys/note.h>
3930588217SMike Christensen #include <sys/mach_descrip.h>
4030588217SMike Christensen #include <sys/mdesc.h>
4130588217SMike Christensen #include <sys/ldc.h>
4230588217SMike Christensen #include <sys/ds.h>
4330588217SMike Christensen #include <sys/ds_impl.h>
4430588217SMike Christensen 
4530588217SMike Christensen #ifndef MIN
4630588217SMike Christensen #define	MIN(a, b)	((a) < (b) ? (a) : (b))
4730588217SMike Christensen #endif
4830588217SMike Christensen 
4930588217SMike Christensen #define	DS_DECODE_BUF_LEN		30
5030588217SMike Christensen 
5130588217SMike Christensen /*
5230588217SMike Christensen  * All DS ports in the system
5330588217SMike Christensen  *
5430588217SMike Christensen  * The list of DS ports is read in from the MD when the DS module is
5530588217SMike Christensen  * initialized and is never modified. This eliminates the need for
5630588217SMike Christensen  * locking to access the port array itself. Access to the individual
5730588217SMike Christensen  * ports are synchronized at the port level.
5830588217SMike Christensen  */
5930588217SMike Christensen ds_port_t	ds_ports[DS_MAX_PORTS];
6030588217SMike Christensen ds_portset_t	ds_allports;	/* all DS ports in the system */
613ef557bfSMike Christensen ds_portset_t	ds_nullport;	/* allows test against null portset */
6230588217SMike Christensen 
6340c61268SMike Christensen /* DS SP port id */
6440c61268SMike Christensen uint64_t ds_sp_port_id = DS_PORTID_INVALID;
6540c61268SMike Christensen 
6630588217SMike Christensen /*
6730588217SMike Christensen  * Table of registered services
6830588217SMike Christensen  *
6930588217SMike Christensen  * Locking: Accesses to the table of services are synchronized using
7030588217SMike Christensen  *   a mutex lock. The reader lock must be held when looking up service
7130588217SMike Christensen  *   information in the table. The writer lock must be held when any
7230588217SMike Christensen  *   service information is being modified.
7330588217SMike Christensen  */
7430588217SMike Christensen ds_svcs_t	ds_svcs;
7530588217SMike Christensen 
7630588217SMike Christensen /*
7730588217SMike Christensen  * Flag to prevent callbacks while in the middle of DS teardown.
7830588217SMike Christensen  */
7930588217SMike Christensen boolean_t ds_enabled = B_FALSE;	/* enable/disable taskq processing */
8030588217SMike Christensen 
8130588217SMike Christensen /*
8230588217SMike Christensen  * Retry count and delay for LDC reads and writes
8330588217SMike Christensen  */
8430588217SMike Christensen #ifndef DS_DEFAULT_RETRIES
8530588217SMike Christensen #define	DS_DEFAULT_RETRIES	10000	/* number of times to retry */
8630588217SMike Christensen #endif
8730588217SMike Christensen #ifndef DS_DEFAULT_DELAY
8830588217SMike Christensen #define	DS_DEFAULT_DELAY	1000	/* usecs to wait between retries */
8930588217SMike Christensen #endif
9030588217SMike Christensen 
9130588217SMike Christensen static int ds_retries = DS_DEFAULT_RETRIES;
9230588217SMike Christensen static clock_t ds_delay = DS_DEFAULT_DELAY;
9330588217SMike Christensen 
9430588217SMike Christensen /*
9530588217SMike Christensen  * Supported versions of the DS message protocol
9630588217SMike Christensen  *
9730588217SMike Christensen  * The version array must be sorted in order from the highest
9830588217SMike Christensen  * supported version to the lowest. Support for a particular
9930588217SMike Christensen  * <major>.<minor> version implies all lower minor versions of
10030588217SMike Christensen  * that same major version are supported as well.
10130588217SMike Christensen  */
10230588217SMike Christensen static ds_ver_t ds_vers[] = { { 1, 0 } };
10330588217SMike Christensen 
10430588217SMike Christensen #define	DS_NUM_VER	(sizeof (ds_vers) / sizeof (ds_vers[0]))
10530588217SMike Christensen 
10630588217SMike Christensen 
10730588217SMike Christensen /* incoming message handling functions */
10830588217SMike Christensen typedef void (*ds_msg_handler_t)(ds_port_t *port, caddr_t buf, size_t len);
10930588217SMike Christensen static void ds_handle_init_req(ds_port_t *port, caddr_t buf, size_t len);
11030588217SMike Christensen static void ds_handle_init_ack(ds_port_t *port, caddr_t buf, size_t len);
11130588217SMike Christensen static void ds_handle_init_nack(ds_port_t *port, caddr_t buf, size_t len);
11230588217SMike Christensen static void ds_handle_reg_req(ds_port_t *port, caddr_t buf, size_t len);
11330588217SMike Christensen static void ds_handle_reg_ack(ds_port_t *port, caddr_t buf, size_t len);
11430588217SMike Christensen static void ds_handle_reg_nack(ds_port_t *port, caddr_t buf, size_t len);
11530588217SMike Christensen static void ds_handle_unreg_req(ds_port_t *port, caddr_t buf, size_t len);
11630588217SMike Christensen static void ds_handle_unreg_ack(ds_port_t *port, caddr_t buf, size_t len);
11730588217SMike Christensen static void ds_handle_unreg_nack(ds_port_t *port, caddr_t buf, size_t len);
11830588217SMike Christensen static void ds_handle_data(ds_port_t *port, caddr_t buf, size_t len);
11930588217SMike Christensen static void ds_handle_nack(ds_port_t *port, caddr_t buf, size_t len);
12030588217SMike Christensen 
12130588217SMike Christensen /*
12230588217SMike Christensen  * DS Message Handler Dispatch Table
12330588217SMike Christensen  *
12430588217SMike Christensen  * A table used to dispatch all incoming messages. This table
12530588217SMike Christensen  * contains handlers for all the fixed message types, as well as
12630588217SMike Christensen  * the the messages defined in the 1.0 version of the DS protocol.
12730588217SMike Christensen  * The handlers are indexed based on the DS header msg_type values
12830588217SMike Christensen  */
12930588217SMike Christensen static const ds_msg_handler_t ds_msg_handlers[] = {
13030588217SMike Christensen 	ds_handle_init_req,		/* DS_INIT_REQ */
13130588217SMike Christensen 	ds_handle_init_ack,		/* DS_INIT_ACK */
13230588217SMike Christensen 	ds_handle_init_nack,		/* DS_INIT_NACK */
13330588217SMike Christensen 	ds_handle_reg_req,		/* DS_REG_REQ */
13430588217SMike Christensen 	ds_handle_reg_ack,		/* DS_REG_ACK */
13530588217SMike Christensen 	ds_handle_reg_nack,		/* DS_REG_NACK */
13630588217SMike Christensen 	ds_handle_unreg_req,		/* DS_UNREG */
13730588217SMike Christensen 	ds_handle_unreg_ack,		/* DS_UNREG_ACK */
13830588217SMike Christensen 	ds_handle_unreg_nack,		/* DS_UNREG_NACK */
13930588217SMike Christensen 	ds_handle_data,			/* DS_DATA */
14030588217SMike Christensen 	ds_handle_nack			/* DS_NACK */
14130588217SMike Christensen };
14230588217SMike Christensen 
14330588217SMike Christensen 
14430588217SMike Christensen 
14530588217SMike Christensen /* initialization functions */
14630588217SMike Christensen static int ds_ldc_init(ds_port_t *port);
14730588217SMike Christensen 
14830588217SMike Christensen /* event processing functions */
14930588217SMike Christensen static uint_t ds_ldc_cb(uint64_t event, caddr_t arg);
15030588217SMike Christensen static int ds_recv_msg(ds_port_t *port, caddr_t msgp, size_t *sizep);
15130588217SMike Christensen static void ds_handle_up_event(ds_port_t *port);
15230588217SMike Christensen static void ds_handle_down_reset_events(ds_port_t *port);
15330588217SMike Christensen static void ds_handle_recv(void *arg);
15430588217SMike Christensen static void ds_dispatch_event(void *arg);
15530588217SMike Christensen 
15630588217SMike Christensen /* message sending functions */
15730588217SMike Christensen static int ds_send_msg(ds_port_t *port, caddr_t msg, size_t msglen);
15830588217SMike Christensen static int ds_send_reg_req(ds_svc_t *svc, ds_port_t *port);
15930588217SMike Christensen static void ds_send_unreg_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl);
16030588217SMike Christensen static void ds_send_data_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl);
16130588217SMike Christensen 
16230588217SMike Christensen /* walker functions */
16330588217SMike Christensen static int ds_svc_isfree(ds_svc_t *svc, void *arg);
16430588217SMike Christensen static int ds_svc_unregister(ds_svc_t *svc, void *arg);
16530588217SMike Christensen static int ds_svc_port_up(ds_svc_t *svc, void *arg);
16630588217SMike Christensen 
16730588217SMike Christensen /* service utilities */
16830588217SMike Christensen static void ds_reset_svc(ds_svc_t *svc, ds_port_t *port);
16930588217SMike Christensen static int ds_svc_register_onport(ds_svc_t *svc, ds_port_t *port);
170ffc2bef0SMike Christensen static int ds_svc_register_onport_walker(ds_svc_t *svc, void *arg);
171ffc2bef0SMike Christensen static void ds_set_port_ready(ds_port_t *port, uint16_t major, uint16_t minor);
17230588217SMike Christensen 
17330588217SMike Christensen /* port utilities */
17430588217SMike Christensen static void ds_port_reset(ds_port_t *port);
17530588217SMike Christensen static ldc_status_t ds_update_ldc_state(ds_port_t *port);
17630588217SMike Christensen 
17730588217SMike Christensen /* misc utilities */
17830588217SMike Christensen static void min_max_versions(int num_versions, ds_ver_t *sup_versionsp,
17930588217SMike Christensen     uint16_t *min_major, uint16_t *max_major);
18030588217SMike Christensen 
18130588217SMike Christensen /* debug */
18230588217SMike Christensen static char *decode_ldc_events(uint64_t event, char *buf);
18330588217SMike Christensen 
18430588217SMike Christensen /* loopback */
18530588217SMike Christensen static void ds_loopback_register(ds_svc_hdl_t hdl);
18630588217SMike Christensen static void ds_loopback_unregister(ds_svc_hdl_t hdl);
18730588217SMike Christensen static void ds_loopback_send(ds_svc_hdl_t hdl, void *buf, size_t buflen);
188a600f50dSMike Christensen static int ds_loopback_set_svc(ds_svc_t *svc, ds_capability_t *cap,
189a600f50dSMike Christensen     ds_svc_hdl_t *lb_hdlp);
19030588217SMike Christensen 
19130588217SMike Christensen /* client handling */
19230588217SMike Christensen static int i_ds_hdl_lookup(char *service, uint_t is_client, ds_svc_hdl_t *hdlp,
19330588217SMike Christensen     uint_t maxhdls);
19430588217SMike Christensen static ds_svc_t *ds_find_clnt_svc_by_hdl_port(ds_svc_hdl_t hdl,
19530588217SMike Christensen     ds_port_t *port);
19630588217SMike Christensen static ds_svc_t *ds_find_svc_by_id_port(char *svc_id, int is_client,
19730588217SMike Christensen     ds_port_t *port);
19830588217SMike Christensen static ds_svc_t *ds_svc_clone(ds_svc_t *svc);
19930588217SMike Christensen static void ds_check_for_dup_services(ds_svc_t *svc);
20030588217SMike Christensen static void ds_delete_svc_entry(ds_svc_t *svc);
20130588217SMike Christensen 
20230588217SMike Christensen char *
ds_strdup(char * str)20330588217SMike Christensen ds_strdup(char *str)
20430588217SMike Christensen {
20530588217SMike Christensen 	char *newstr;
20630588217SMike Christensen 
20730588217SMike Christensen 	newstr = DS_MALLOC(strlen(str) + 1);
20830588217SMike Christensen 	(void) strcpy(newstr, str);
20930588217SMike Christensen 	return (newstr);
21030588217SMike Christensen }
21130588217SMike Christensen 
21230588217SMike Christensen void
ds_common_init(void)21330588217SMike Christensen ds_common_init(void)
21430588217SMike Christensen {
21530588217SMike Christensen 	/* Validate version table */
21630588217SMike Christensen 	ASSERT(ds_vers_isvalid(ds_vers, DS_NUM_VER) == DS_VERS_OK);
21730588217SMike Christensen 
21830588217SMike Christensen 	/* Initialize services table */
21930588217SMike Christensen 	ds_init_svcs_tbl(DS_MAXSVCS_INIT);
22030588217SMike Christensen 
22130588217SMike Christensen 	/* enable callback processing */
22230588217SMike Christensen 	ds_enabled = B_TRUE;
22330588217SMike Christensen }
22430588217SMike Christensen 
22530588217SMike Christensen /* BEGIN LDC SUPPORT FUNCTIONS */
22630588217SMike Christensen 
22730588217SMike Christensen static char *
decode_ldc_events(uint64_t event,char * buf)22830588217SMike Christensen decode_ldc_events(uint64_t event, char *buf)
22930588217SMike Christensen {
23030588217SMike Christensen 	buf[0] = 0;
23130588217SMike Christensen 	if (event & LDC_EVT_DOWN)	(void) strcat(buf, " DOWN");
23230588217SMike Christensen 	if (event & LDC_EVT_RESET)	(void) strcat(buf, " RESET");
23330588217SMike Christensen 	if (event & LDC_EVT_UP)		(void) strcat(buf, " UP");
23430588217SMike Christensen 	if (event & LDC_EVT_READ)	(void) strcat(buf, " READ");
23530588217SMike Christensen 	if (event & LDC_EVT_WRITE)	(void) strcat(buf, " WRITE");
23630588217SMike Christensen 	return (buf);
23730588217SMike Christensen }
23830588217SMike Christensen 
23930588217SMike Christensen static ldc_status_t
ds_update_ldc_state(ds_port_t * port)24030588217SMike Christensen ds_update_ldc_state(ds_port_t *port)
24130588217SMike Christensen {
24230588217SMike Christensen 	ldc_status_t	ldc_state;
24330588217SMike Christensen 	int		rv;
24430588217SMike Christensen 	char		ebuf[DS_EBUFSIZE];
24530588217SMike Christensen 
24630588217SMike Christensen 	ASSERT(MUTEX_HELD(&port->lock));
24730588217SMike Christensen 
24830588217SMike Christensen 	/*
24930588217SMike Christensen 	 * Read status and update ldc state info in port structure.
25030588217SMike Christensen 	 */
25130588217SMike Christensen 	if ((rv = ldc_status(port->ldc.hdl, &ldc_state)) != 0) {
25230588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_status error: %s" DS_EOL,
25330588217SMike Christensen 		    PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
25430588217SMike Christensen 		ldc_state = port->ldc.state;
25530588217SMike Christensen 	} else {
25630588217SMike Christensen 		port->ldc.state = ldc_state;
25730588217SMike Christensen 	}
25830588217SMike Christensen 
25930588217SMike Christensen 	return (ldc_state);
26030588217SMike Christensen }
26130588217SMike Christensen 
26230588217SMike Christensen static void
ds_handle_down_reset_events(ds_port_t * port)26330588217SMike Christensen ds_handle_down_reset_events(ds_port_t *port)
26430588217SMike Christensen {
26530588217SMike Christensen 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: entered" DS_EOL, PORTID(port),
26630588217SMike Christensen 	    __func__);
26730588217SMike Christensen 
26830588217SMike Christensen 	mutex_enter(&ds_svcs.lock);
26930588217SMike Christensen 	mutex_enter(&port->lock);
27030588217SMike Christensen 
27130588217SMike Christensen 	ds_sys_drain_events(port);
27230588217SMike Christensen 
27330588217SMike Christensen 	(void) ds_update_ldc_state(port);
27430588217SMike Christensen 
27530588217SMike Christensen 	/* reset the port state */
27630588217SMike Christensen 	ds_port_reset(port);
27730588217SMike Christensen 
27830588217SMike Christensen 	/* acknowledge the reset */
27930588217SMike Christensen 	(void) ldc_up(port->ldc.hdl);
28030588217SMike Christensen 
28130588217SMike Christensen 	mutex_exit(&port->lock);
28230588217SMike Christensen 	mutex_exit(&ds_svcs.lock);
28330588217SMike Christensen 
28430588217SMike Christensen 	ds_handle_up_event(port);
28530588217SMike Christensen 
28630588217SMike Christensen 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: exit" DS_EOL, PORTID(port), __func__);
28730588217SMike Christensen }
28830588217SMike Christensen 
28930588217SMike Christensen static void
ds_handle_up_event(ds_port_t * port)29030588217SMike Christensen ds_handle_up_event(ds_port_t *port)
29130588217SMike Christensen {
29230588217SMike Christensen 	ldc_status_t	ldc_state;
29330588217SMike Christensen 
29430588217SMike Christensen 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: entered" DS_EOL, PORTID(port),
29530588217SMike Christensen 	    __func__);
29630588217SMike Christensen 
29730588217SMike Christensen 	mutex_enter(&port->lock);
29830588217SMike Christensen 
29930588217SMike Christensen 	ldc_state = ds_update_ldc_state(port);
30030588217SMike Christensen 
30130588217SMike Christensen 	mutex_exit(&port->lock);
30230588217SMike Christensen 
30330588217SMike Christensen 	if ((ldc_state == LDC_UP) && IS_DS_PORT(port)) {
30430588217SMike Christensen 		/*
30530588217SMike Christensen 		 * Initiate the handshake.
30630588217SMike Christensen 		 */
30730588217SMike Christensen 		ds_send_init_req(port);
30830588217SMike Christensen 	}
30930588217SMike Christensen 
31030588217SMike Christensen 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: exit" DS_EOL, PORTID(port), __func__);
31130588217SMike Christensen }
31230588217SMike Christensen 
31330588217SMike Christensen static uint_t
ds_ldc_cb(uint64_t event,caddr_t arg)31430588217SMike Christensen ds_ldc_cb(uint64_t event, caddr_t arg)
31530588217SMike Christensen {
31630588217SMike Christensen 	ds_port_t	*port = (ds_port_t *)arg;
31730588217SMike Christensen 	char		evstring[DS_DECODE_BUF_LEN];
31830588217SMike Christensen 
31930588217SMike Christensen 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: %s event (%llx) received" DS_EOL,
32030588217SMike Christensen 	    PORTID(port), __func__, decode_ldc_events(event, evstring),
32130588217SMike Christensen 	    (u_longlong_t)event);
32230588217SMike Christensen 
32330588217SMike Christensen 	if (!ds_enabled) {
32430588217SMike Christensen 		DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: callback handling is disabled"
32530588217SMike Christensen 		    DS_EOL, PORTID(port), __func__);
32630588217SMike Christensen 		return (LDC_SUCCESS);
32730588217SMike Christensen 	}
32830588217SMike Christensen 
32930588217SMike Christensen 	if (event & (LDC_EVT_DOWN | LDC_EVT_RESET)) {
33030588217SMike Christensen 		ds_handle_down_reset_events(port);
33130588217SMike Christensen 		goto done;
33230588217SMike Christensen 	}
33330588217SMike Christensen 
33430588217SMike Christensen 	if (event & LDC_EVT_UP) {
33530588217SMike Christensen 		ds_handle_up_event(port);
33630588217SMike Christensen 	}
33730588217SMike Christensen 
33830588217SMike Christensen 	if (event & LDC_EVT_READ) {
33930588217SMike Christensen 		if (port->ldc.state != LDC_UP) {
34030588217SMike Christensen 			cmn_err(CE_WARN, "ds@%lx: %s: LDC READ event while "
34130588217SMike Christensen 			    "port not up" DS_EOL, PORTID(port), __func__);
34230588217SMike Christensen 			goto done;
34330588217SMike Christensen 		}
34430588217SMike Christensen 
34530588217SMike Christensen 		if (ds_sys_dispatch_func(ds_handle_recv, port)) {
34630588217SMike Christensen 			cmn_err(CE_WARN, "ds@%lx: error initiating LDC READ "
34730588217SMike Christensen 			    " event", PORTID(port));
34830588217SMike Christensen 		}
34930588217SMike Christensen 	}
35030588217SMike Christensen 
35130588217SMike Christensen 	if (event & LDC_EVT_WRITE) {
35230588217SMike Christensen 		DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: LDC WRITE event received, "
35330588217SMike Christensen 		    "not supported" DS_EOL, PORTID(port), __func__);
35430588217SMike Christensen 	}
35530588217SMike Christensen 
35630588217SMike Christensen 	if (event & ~(LDC_EVT_UP | LDC_EVT_READ)) {
35730588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: %s: Unexpected LDC event received: "
35830588217SMike Christensen 		    "0x%llx" DS_EOL, PORTID(port), __func__,
35930588217SMike Christensen 		    (u_longlong_t)event);
36030588217SMike Christensen 	}
36130588217SMike Christensen done:
36230588217SMike Christensen 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: exit" DS_EOL, PORTID(port), __func__);
36330588217SMike Christensen 
36430588217SMike Christensen 	return (LDC_SUCCESS);
36530588217SMike Christensen }
36630588217SMike Christensen 
36730588217SMike Christensen static int
ds_ldc_init(ds_port_t * port)36830588217SMike Christensen ds_ldc_init(ds_port_t *port)
36930588217SMike Christensen {
37030588217SMike Christensen 	int		rv;
37130588217SMike Christensen 	ldc_attr_t	ldc_attr;
37230588217SMike Christensen 	caddr_t		ldc_cb_arg = (caddr_t)port;
37330588217SMike Christensen 	char		ebuf[DS_EBUFSIZE];
37430588217SMike Christensen 
37530588217SMike Christensen 	ASSERT(MUTEX_HELD(&port->lock));
37630588217SMike Christensen 
37730588217SMike Christensen 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: ldc_id=%lld" DS_EOL,
37830588217SMike Christensen 	    PORTID(port), __func__, (u_longlong_t)port->ldc.id);
37930588217SMike Christensen 
38030588217SMike Christensen 	ldc_attr.devclass = LDC_DEV_GENERIC;
38130588217SMike Christensen 	ldc_attr.instance = 0;
38230588217SMike Christensen 	ldc_attr.mode = LDC_MODE_RELIABLE;
38330588217SMike Christensen 	ldc_attr.mtu = DS_STREAM_MTU;
38430588217SMike Christensen 
38530588217SMike Christensen 	if ((rv = ldc_init(port->ldc.id, &ldc_attr, &port->ldc.hdl)) != 0) {
38630588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_id: %lx, ldc_init error: %s"
38730588217SMike Christensen 		    DS_EOL, PORTID(port), __func__, port->ldc.id,
38830588217SMike Christensen 		    ds_errno_to_str(rv, ebuf));
38930588217SMike Christensen 		return (rv);
39030588217SMike Christensen 	}
39130588217SMike Christensen 
39230588217SMike Christensen 	rv = ldc_reg_callback(port->ldc.hdl, ds_ldc_cb, ldc_cb_arg);
39330588217SMike Christensen 	if (rv != 0) {
39430588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_reg_callback error: %s"
39530588217SMike Christensen 		    DS_EOL, PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
39630588217SMike Christensen 		return (rv);
39730588217SMike Christensen 	}
39830588217SMike Christensen 
39930588217SMike Christensen 	ds_sys_ldc_init(port);
40030588217SMike Christensen 	return (0);
40130588217SMike Christensen }
40230588217SMike Christensen 
40330588217SMike Christensen int
ds_ldc_fini(ds_port_t * port)40430588217SMike Christensen ds_ldc_fini(ds_port_t *port)
40530588217SMike Christensen {
40630588217SMike Christensen 	int	rv;
40730588217SMike Christensen 	char	ebuf[DS_EBUFSIZE];
40830588217SMike Christensen 
40930588217SMike Christensen 	ASSERT(port->state >= DS_PORT_LDC_INIT);
410beba1dd8SMike Christensen 	ASSERT(MUTEX_HELD(&port->lock));
41130588217SMike Christensen 
41230588217SMike Christensen 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: ldc_id=%ld" DS_EOL, PORTID(port),
41330588217SMike Christensen 	    __func__, port->ldc.id);
41430588217SMike Christensen 
41530588217SMike Christensen 	if ((rv = ldc_close(port->ldc.hdl)) != 0) {
41630588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_close error: %s" DS_EOL,
41730588217SMike Christensen 		    PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
41830588217SMike Christensen 		return (rv);
41930588217SMike Christensen 	}
42030588217SMike Christensen 
42130588217SMike Christensen 	if ((rv = ldc_unreg_callback(port->ldc.hdl)) != 0) {
42230588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_unreg_callback error: %s"
42330588217SMike Christensen 		    DS_EOL, PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
42430588217SMike Christensen 		return (rv);
42530588217SMike Christensen 	}
42630588217SMike Christensen 
42730588217SMike Christensen 	if ((rv = ldc_fini(port->ldc.hdl)) != 0) {
42830588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_fini error: %s" DS_EOL,
42930588217SMike Christensen 		    PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
43030588217SMike Christensen 		return (rv);
43130588217SMike Christensen 	}
43230588217SMike Christensen 
433beba1dd8SMike Christensen 	port->ldc.id = (uint64_t)-1;
434*cb453c7aSToomas Soome 	port->ldc.hdl = 0;
435beba1dd8SMike Christensen 	port->ldc.state = 0;
436beba1dd8SMike Christensen 
43730588217SMike Christensen 	return (rv);
43830588217SMike Christensen }
43930588217SMike Christensen 
44030588217SMike Christensen /*
44130588217SMike Christensen  * Attempt to read a specified number of bytes from a particular LDC.
44230588217SMike Christensen  * Returns zero for success or the return code from the LDC read on
44330588217SMike Christensen  * failure. The actual number of bytes read from the LDC is returned
44430588217SMike Christensen  * in the size parameter.
44530588217SMike Christensen  */
44630588217SMike Christensen static int
ds_recv_msg(ds_port_t * port,caddr_t msgp,size_t * sizep)44730588217SMike Christensen ds_recv_msg(ds_port_t *port, caddr_t msgp, size_t *sizep)
44830588217SMike Christensen {
44930588217SMike Christensen 	int	rv = 0;
45030588217SMike Christensen 	size_t	bytes_req = *sizep;
45130588217SMike Christensen 	size_t	bytes_left = bytes_req;
45230588217SMike Christensen 	size_t	nbytes;
45330588217SMike Christensen 	int	retry_count = 0;
45430588217SMike Christensen 	char	ebuf[DS_EBUFSIZE];
45530588217SMike Christensen 
45630588217SMike Christensen 	ASSERT(MUTEX_HELD(&port->rcv_lock));
45730588217SMike Christensen 
45830588217SMike Christensen 	*sizep = 0;
45930588217SMike Christensen 
46030588217SMike Christensen 	DS_DBG_LDC(CE_NOTE, "ds@%lx: attempting to read %ld bytes" DS_EOL,
46130588217SMike Christensen 	    PORTID(port), bytes_req);
46230588217SMike Christensen 
46330588217SMike Christensen 	while (bytes_left > 0) {
46430588217SMike Christensen 
46530588217SMike Christensen 		nbytes = bytes_left;
46630588217SMike Christensen 
467beba1dd8SMike Christensen 		mutex_enter(&port->lock);
468beba1dd8SMike Christensen 		if (port->ldc.state == LDC_UP) {
469beba1dd8SMike Christensen 			rv = ldc_read(port->ldc.hdl, msgp, &nbytes);
470beba1dd8SMike Christensen 		} else
471beba1dd8SMike Christensen 			rv = ENXIO;
472beba1dd8SMike Christensen 		mutex_exit(&port->lock);
473beba1dd8SMike Christensen 		if (rv != 0) {
47430588217SMike Christensen 			if (rv == ECONNRESET) {
47530588217SMike Christensen 				break;
47630588217SMike Christensen 			} else if (rv != EAGAIN) {
47730588217SMike Christensen 				cmn_err(CE_NOTE, "ds@%lx: %s: %s" DS_EOL,
47830588217SMike Christensen 				    PORTID(port), __func__,
47930588217SMike Christensen 				    ds_errno_to_str(rv, ebuf));
48030588217SMike Christensen 				break;
48130588217SMike Christensen 			}
48230588217SMike Christensen 		} else {
48330588217SMike Christensen 			if (nbytes != 0) {
48430588217SMike Christensen 				DS_DBG_LDC(CE_NOTE, "ds@%lx: "
48530588217SMike Christensen 				    "read %ld bytes, %d retries" DS_EOL,
48630588217SMike Christensen 				    PORTID(port), nbytes, retry_count);
48730588217SMike Christensen 
48830588217SMike Christensen 				*sizep += nbytes;
48930588217SMike Christensen 				msgp += nbytes;
49030588217SMike Christensen 				bytes_left -= nbytes;
49130588217SMike Christensen 
49230588217SMike Christensen 				/* reset counter on a successful read */
49330588217SMike Christensen 				retry_count = 0;
49430588217SMike Christensen 				continue;
49530588217SMike Christensen 			}
49630588217SMike Christensen 
49730588217SMike Christensen 			/*
49830588217SMike Christensen 			 * No data was read. Check if this is the
49930588217SMike Christensen 			 * first attempt. If so, just return since
50030588217SMike Christensen 			 * nothing has been read yet.
50130588217SMike Christensen 			 */
50230588217SMike Christensen 			if (bytes_left == bytes_req) {
50330588217SMike Christensen 				DS_DBG_LDC(CE_NOTE, "ds@%lx: read zero bytes, "
50430588217SMike Christensen 				    " no data available" DS_EOL, PORTID(port));
50530588217SMike Christensen 				break;
50630588217SMike Christensen 			}
50730588217SMike Christensen 		}
50830588217SMike Christensen 
50930588217SMike Christensen 		/*
51030588217SMike Christensen 		 * A retry is necessary because the read returned
51130588217SMike Christensen 		 * EAGAIN, or a zero length read occurred after
51230588217SMike Christensen 		 * reading a partial message.
51330588217SMike Christensen 		 */
51430588217SMike Christensen 		if (retry_count++ >= ds_retries) {
51530588217SMike Christensen 			DS_DBG_LDC(CE_NOTE, "ds@%lx: timed out waiting for "
51630588217SMike Christensen 			    "message" DS_EOL, PORTID(port));
51730588217SMike Christensen 			break;
51830588217SMike Christensen 		}
51930588217SMike Christensen 
52030588217SMike Christensen 		drv_usecwait(ds_delay);
52130588217SMike Christensen 	}
52230588217SMike Christensen 
52330588217SMike Christensen 	return (rv);
52430588217SMike Christensen }
52530588217SMike Christensen 
52630588217SMike Christensen static void
ds_handle_recv(void * arg)52730588217SMike Christensen ds_handle_recv(void *arg)
52830588217SMike Christensen {
52930588217SMike Christensen 	ds_port_t	*port = (ds_port_t *)arg;
53030588217SMike Christensen 	char		*hbuf;
53130588217SMike Christensen 	size_t		msglen;
53230588217SMike Christensen 	size_t		read_size;
53330588217SMike Christensen 	boolean_t	hasdata;
53430588217SMike Christensen 	ds_hdr_t	hdr;
53530588217SMike Christensen 	uint8_t		*msg;
53630588217SMike Christensen 	char		*currp;
53730588217SMike Christensen 	int		rv;
53830588217SMike Christensen 	ds_event_t	*devent;
53930588217SMike Christensen 
54030588217SMike Christensen 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s..." DS_EOL, PORTID(port), __func__);
54130588217SMike Christensen 
54230588217SMike Christensen 	/*
54330588217SMike Christensen 	 * Read messages from the channel until there are none
54430588217SMike Christensen 	 * pending. Valid messages are dispatched to be handled
54530588217SMike Christensen 	 * by a separate thread while any malformed messages are
54630588217SMike Christensen 	 * dropped.
54730588217SMike Christensen 	 */
54830588217SMike Christensen 
54930588217SMike Christensen 	mutex_enter(&port->rcv_lock);
55030588217SMike Christensen 
551beba1dd8SMike Christensen 	for (;;) {
552beba1dd8SMike Christensen 		mutex_enter(&port->lock);
553beba1dd8SMike Christensen 		if (port->ldc.state == LDC_UP) {
554beba1dd8SMike Christensen 			rv = ldc_chkq(port->ldc.hdl, &hasdata);
555beba1dd8SMike Christensen 		} else
556beba1dd8SMike Christensen 			rv = ENXIO;
557beba1dd8SMike Christensen 		mutex_exit(&port->lock);
558beba1dd8SMike Christensen 		if (rv != 0 || !hasdata)
559beba1dd8SMike Christensen 			break;
56030588217SMike Christensen 
56130588217SMike Christensen 		DS_DBG(CE_NOTE, "ds@%lx: %s: reading next message" DS_EOL,
56230588217SMike Christensen 		    PORTID(port), __func__);
56330588217SMike Christensen 
56430588217SMike Christensen 		/*
56530588217SMike Christensen 		 * Read in the next message.
56630588217SMike Christensen 		 */
56730588217SMike Christensen 		hbuf = (char *)&hdr;
56830588217SMike Christensen 		bzero(hbuf, DS_HDR_SZ);
56930588217SMike Christensen 		read_size = DS_HDR_SZ;
57030588217SMike Christensen 		currp = hbuf;
57130588217SMike Christensen 
57230588217SMike Christensen 		/* read in the message header */
57330588217SMike Christensen 		if ((rv = ds_recv_msg(port, currp, &read_size)) != 0) {
57430588217SMike Christensen 			break;
57530588217SMike Christensen 		}
57630588217SMike Christensen 
57730588217SMike Christensen 		if (read_size < DS_HDR_SZ) {
57830588217SMike Christensen 			/*
57930588217SMike Christensen 			 * A zero length read is a valid signal that
58030588217SMike Christensen 			 * there is no data left on the channel.
58130588217SMike Christensen 			 */
58230588217SMike Christensen 			if (read_size != 0) {
58330588217SMike Christensen 				cmn_err(CE_WARN, "ds@%lx: invalid message "
58430588217SMike Christensen 				    "length, received %ld bytes, expected %ld"
58530588217SMike Christensen 				    DS_EOL, PORTID(port), read_size, DS_HDR_SZ);
58630588217SMike Christensen 			}
58730588217SMike Christensen 			continue;
58830588217SMike Christensen 		}
58930588217SMike Christensen 
59030588217SMike Christensen 		/* get payload size and allocate a buffer */
59130588217SMike Christensen 		read_size = ((ds_hdr_t *)hbuf)->payload_len;
59230588217SMike Christensen 		msglen = DS_HDR_SZ + read_size;
59330588217SMike Christensen 		msg = DS_MALLOC(msglen);
59430588217SMike Christensen 		if (!msg) {
59530588217SMike Christensen 			cmn_err(CE_WARN, "Memory allocation failed attempting "
59630588217SMike Christensen 			    " to allocate %d bytes." DS_EOL, (int)msglen);
59730588217SMike Christensen 			continue;
59830588217SMike Christensen 		}
59930588217SMike Christensen 
60030588217SMike Christensen 		DS_DBG(CE_NOTE, "ds@%lx: %s: message payload len %d" DS_EOL,
60130588217SMike Christensen 		    PORTID(port), __func__, (int)read_size);
60230588217SMike Christensen 
60330588217SMike Christensen 		/* move message header into buffer */
60430588217SMike Christensen 		(void) memcpy(msg, hbuf, DS_HDR_SZ);
60530588217SMike Christensen 		currp = (char *)(msg) + DS_HDR_SZ;
60630588217SMike Christensen 
60730588217SMike Christensen 		/* read in the message body */
60830588217SMike Christensen 		if ((rv = ds_recv_msg(port, currp, &read_size)) != 0) {
60930588217SMike Christensen 			DS_FREE(msg, msglen);
61030588217SMike Christensen 			break;
61130588217SMike Christensen 		}
61230588217SMike Christensen 
61330588217SMike Christensen 		/* validate the size of the message */
61430588217SMike Christensen 		if ((DS_HDR_SZ + read_size) != msglen) {
61530588217SMike Christensen 			cmn_err(CE_WARN, "ds@%lx: %s: invalid message length, "
61630588217SMike Christensen 			    "received %ld bytes, expected %ld" DS_EOL,
61730588217SMike Christensen 			    PORTID(port), __func__, (DS_HDR_SZ + read_size),
61830588217SMike Christensen 			    msglen);
61930588217SMike Christensen 			DS_FREE(msg, msglen);
62030588217SMike Christensen 			continue;
62130588217SMike Christensen 		}
62230588217SMike Christensen 
62330588217SMike Christensen 		DS_DUMP_MSG(DS_DBG_FLAG_LDC, msg, msglen);
62430588217SMike Christensen 
62530588217SMike Christensen 		/*
62630588217SMike Christensen 		 * Send the message for processing, and store it
62730588217SMike Christensen 		 * in the log. The memory is deallocated only when
62830588217SMike Christensen 		 * the message is removed from the log.
62930588217SMike Christensen 		 */
63030588217SMike Christensen 
63130588217SMike Christensen 		devent = DS_MALLOC(sizeof (ds_event_t));
63230588217SMike Christensen 		devent->port = port;
63330588217SMike Christensen 		devent->buf = (char *)msg;
63430588217SMike Christensen 		devent->buflen = msglen;
63530588217SMike Christensen 
63630588217SMike Christensen 		/* log the message */
63730588217SMike Christensen 		(void) ds_log_add_msg(DS_LOG_IN(port->id), msg, msglen);
63830588217SMike Christensen 
63930588217SMike Christensen 		if (ds_sys_dispatch_func(ds_dispatch_event, devent)) {
64030588217SMike Christensen 			cmn_err(CE_WARN, "ds@%lx: error initiating "
64130588217SMike Christensen 			    "event handler", PORTID(port));
64230588217SMike Christensen 			DS_FREE(devent, sizeof (ds_event_t));
64330588217SMike Christensen 		}
64430588217SMike Christensen 	}
64530588217SMike Christensen 
64630588217SMike Christensen 	mutex_exit(&port->rcv_lock);
64730588217SMike Christensen 
64830588217SMike Christensen 	/* handle connection reset errors returned from ds_recv_msg */
64930588217SMike Christensen 	if (rv == ECONNRESET) {
65030588217SMike Christensen 		ds_handle_down_reset_events(port);
65130588217SMike Christensen 	}
65230588217SMike Christensen 
65330588217SMike Christensen 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s done" DS_EOL, PORTID(port), __func__);
65430588217SMike Christensen }
65530588217SMike Christensen 
65630588217SMike Christensen static void
ds_dispatch_event(void * arg)65730588217SMike Christensen ds_dispatch_event(void *arg)
65830588217SMike Christensen {
65930588217SMike Christensen 	ds_event_t	*event = (ds_event_t *)arg;
66030588217SMike Christensen 	ds_hdr_t	*hdr;
66130588217SMike Christensen 	ds_port_t	*port;
66230588217SMike Christensen 
66330588217SMike Christensen 	port = event->port;
66430588217SMike Christensen 
66530588217SMike Christensen 	hdr = (ds_hdr_t *)event->buf;
66630588217SMike Christensen 
66730588217SMike Christensen 	if (DS_MSG_TYPE_VALID(hdr->msg_type)) {
66830588217SMike Christensen 		DS_DBG(CE_NOTE, "ds@%lx: dispatch_event: msg_type=%d" DS_EOL,
66930588217SMike Christensen 		    PORTID(port), hdr->msg_type);
67030588217SMike Christensen 
67130588217SMike Christensen 		(*ds_msg_handlers[hdr->msg_type])(port, event->buf,
67230588217SMike Christensen 		    event->buflen);
67330588217SMike Christensen 	} else {
67430588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: dispatch_event: invalid msg "
67530588217SMike Christensen 		    "type (%d)" DS_EOL, PORTID(port), hdr->msg_type);
67630588217SMike Christensen 	}
67730588217SMike Christensen 
67830588217SMike Christensen 	DS_FREE(event->buf, event->buflen);
67930588217SMike Christensen 	DS_FREE(event, sizeof (ds_event_t));
68030588217SMike Christensen }
68130588217SMike Christensen 
68230588217SMike Christensen int
ds_send_msg(ds_port_t * port,caddr_t msg,size_t msglen)68330588217SMike Christensen ds_send_msg(ds_port_t *port, caddr_t msg, size_t msglen)
68430588217SMike Christensen {
68530588217SMike Christensen 	int	rv;
68630588217SMike Christensen 	caddr_t	currp = msg;
68730588217SMike Christensen 	size_t	amt_left = msglen;
68830588217SMike Christensen 	int	loopcnt = 0;
68930588217SMike Christensen 
69030588217SMike Christensen 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s msglen: %ld" DS_EOL, PORTID(port),
69130588217SMike Christensen 	    __func__, msglen);
69230588217SMike Christensen 	DS_DUMP_MSG(DS_DBG_FLAG_LDC, msg, msglen);
69330588217SMike Christensen 
694ffc2bef0SMike Christensen 	(void) ds_log_add_msg(DS_LOG_OUT(port->id), (uint8_t *)msg, msglen);
695ffc2bef0SMike Christensen 
69630588217SMike Christensen 	/*
69730588217SMike Christensen 	 * Ensure that no other messages can be sent on this port by holding
69830588217SMike Christensen 	 * the tx_lock mutex in case the write doesn't get sent with one write.
69930588217SMike Christensen 	 * This guarantees that the message doesn't become fragmented.
70030588217SMike Christensen 	 */
70130588217SMike Christensen 	mutex_enter(&port->tx_lock);
70230588217SMike Christensen 
70330588217SMike Christensen 	do {
704beba1dd8SMike Christensen 		mutex_enter(&port->lock);
705beba1dd8SMike Christensen 		if (port->ldc.state == LDC_UP) {
706beba1dd8SMike Christensen 			rv = ldc_write(port->ldc.hdl, currp, &msglen);
707beba1dd8SMike Christensen 		} else
708beba1dd8SMike Christensen 			rv = ENXIO;
709beba1dd8SMike Christensen 		mutex_exit(&port->lock);
710beba1dd8SMike Christensen 		if (rv != 0) {
71130588217SMike Christensen 			if (rv == ECONNRESET) {
71230588217SMike Christensen 				mutex_exit(&port->tx_lock);
713da42b56aSMike Christensen 				(void) ds_sys_dispatch_func((void (*)(void *))
714da42b56aSMike Christensen 				    ds_handle_down_reset_events, port);
71530588217SMike Christensen 				return (rv);
71630588217SMike Christensen 			} else if ((rv == EWOULDBLOCK) &&
71730588217SMike Christensen 			    (loopcnt++ < ds_retries)) {
71830588217SMike Christensen 				drv_usecwait(ds_delay);
71930588217SMike Christensen 			} else {
720ffc2bef0SMike Christensen 				DS_DBG_PRCL(CE_NOTE, "ds@%lx: send_msg: "
721ffc2bef0SMike Christensen 				    "ldc_write failed (%d), %d bytes "
722ffc2bef0SMike Christensen 				    "remaining" DS_EOL, PORTID(port), rv,
723ffc2bef0SMike Christensen 				    (int)amt_left);
72430588217SMike Christensen 				goto error;
72530588217SMike Christensen 			}
72630588217SMike Christensen 		} else {
72730588217SMike Christensen 			amt_left -= msglen;
72830588217SMike Christensen 			currp += msglen;
72930588217SMike Christensen 			msglen = amt_left;
73030588217SMike Christensen 			loopcnt = 0;
73130588217SMike Christensen 		}
73230588217SMike Christensen 	} while (amt_left > 0);
73330588217SMike Christensen error:
73430588217SMike Christensen 	mutex_exit(&port->tx_lock);
73530588217SMike Christensen 
73630588217SMike Christensen 	return (rv);
73730588217SMike Christensen }
73830588217SMike Christensen 
73930588217SMike Christensen /* END LDC SUPPORT FUNCTIONS */
74030588217SMike Christensen 
74130588217SMike Christensen 
74230588217SMike Christensen /* BEGIN DS PROTOCOL SUPPORT FUNCTIONS */
74330588217SMike Christensen 
74430588217SMike Christensen static void
ds_handle_init_req(ds_port_t * port,caddr_t buf,size_t len)74530588217SMike Christensen ds_handle_init_req(ds_port_t *port, caddr_t buf, size_t len)
74630588217SMike Christensen {
74730588217SMike Christensen 	ds_hdr_t	*hdr;
74830588217SMike Christensen 	ds_init_ack_t	*ack;
74930588217SMike Christensen 	ds_init_nack_t	*nack;
75030588217SMike Christensen 	char		*msg;
75130588217SMike Christensen 	size_t		msglen;
75230588217SMike Christensen 	ds_init_req_t	*req;
75330588217SMike Christensen 	size_t		explen = DS_MSG_LEN(ds_init_req_t);
75430588217SMike Christensen 	uint16_t	new_major;
75530588217SMike Christensen 	uint16_t	new_minor;
75630588217SMike Christensen 	boolean_t	match;
75730588217SMike Christensen 
75830588217SMike Christensen 	/* sanity check the incoming message */
75930588217SMike Christensen 	if (len != explen) {
76030588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: <init_req: invalid message "
76130588217SMike Christensen 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
76230588217SMike Christensen 		    explen);
76330588217SMike Christensen 		return;
76430588217SMike Christensen 	}
76530588217SMike Christensen 
76630588217SMike Christensen 	req = (ds_init_req_t *)(buf + DS_HDR_SZ);
76730588217SMike Christensen 
76830588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_req: ver=%d.%d" DS_EOL,
76930588217SMike Christensen 	    PORTID(port), req->major_vers, req->minor_vers);
77030588217SMike Christensen 
77130588217SMike Christensen 	match = negotiate_version(DS_NUM_VER, &ds_vers[0],
77230588217SMike Christensen 	    req->major_vers, &new_major, &new_minor);
77330588217SMike Christensen 
77430588217SMike Christensen 	/*
77530588217SMike Christensen 	 * Check version info. ACK only if the major numbers exactly
77630588217SMike Christensen 	 * match. The service entity can retry with a new minor
77730588217SMike Christensen 	 * based on the response sent as part of the NACK.
77830588217SMike Christensen 	 */
77930588217SMike Christensen 	if (match) {
78030588217SMike Christensen 		msglen = DS_MSG_LEN(ds_init_ack_t);
78130588217SMike Christensen 		msg = DS_MALLOC(msglen);
78230588217SMike Christensen 
78330588217SMike Christensen 		hdr = (ds_hdr_t *)msg;
78430588217SMike Christensen 		hdr->msg_type = DS_INIT_ACK;
78530588217SMike Christensen 		hdr->payload_len = sizeof (ds_init_ack_t);
78630588217SMike Christensen 
78730588217SMike Christensen 		ack = (ds_init_ack_t *)(msg + DS_HDR_SZ);
78830588217SMike Christensen 		ack->minor_vers = MIN(new_minor, req->minor_vers);
78930588217SMike Christensen 
79030588217SMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_ack>: minor=0x%04X" DS_EOL,
79130588217SMike Christensen 		    PORTID(port), MIN(new_minor, req->minor_vers));
79230588217SMike Christensen 	} else {
79330588217SMike Christensen 		msglen = DS_MSG_LEN(ds_init_nack_t);
79430588217SMike Christensen 		msg = DS_MALLOC(msglen);
79530588217SMike Christensen 
79630588217SMike Christensen 		hdr = (ds_hdr_t *)msg;
79730588217SMike Christensen 		hdr->msg_type = DS_INIT_NACK;
79830588217SMike Christensen 		hdr->payload_len = sizeof (ds_init_nack_t);
79930588217SMike Christensen 
80030588217SMike Christensen 		nack = (ds_init_nack_t *)(msg + DS_HDR_SZ);
80130588217SMike Christensen 		nack->major_vers = new_major;
80230588217SMike Christensen 
80330588217SMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_nack>: major=0x%04X" DS_EOL,
80430588217SMike Christensen 		    PORTID(port), new_major);
80530588217SMike Christensen 	}
80630588217SMike Christensen 
80730588217SMike Christensen 	/*
80830588217SMike Christensen 	 * Send the response
80930588217SMike Christensen 	 */
81030588217SMike Christensen 	(void) ds_send_msg(port, msg, msglen);
81130588217SMike Christensen 	DS_FREE(msg, msglen);
812ffc2bef0SMike Christensen 
813ffc2bef0SMike Christensen 	if (match) {
814ffc2bef0SMike Christensen 		ds_set_port_ready(port, req->major_vers, ack->minor_vers);
815ffc2bef0SMike Christensen 	}
81630588217SMike Christensen }
81730588217SMike Christensen 
81830588217SMike Christensen static void
ds_handle_init_ack(ds_port_t * port,caddr_t buf,size_t len)81930588217SMike Christensen ds_handle_init_ack(ds_port_t *port, caddr_t buf, size_t len)
82030588217SMike Christensen {
82130588217SMike Christensen 	ds_init_ack_t	*ack;
82230588217SMike Christensen 	ds_ver_t	*ver;
823ffc2bef0SMike Christensen 	uint16_t	major;
824ffc2bef0SMike Christensen 	uint16_t	minor;
82530588217SMike Christensen 	size_t		explen = DS_MSG_LEN(ds_init_ack_t);
82630588217SMike Christensen 
82730588217SMike Christensen 	/* sanity check the incoming message */
82830588217SMike Christensen 	if (len != explen) {
82930588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: <init_ack: invalid message "
83030588217SMike Christensen 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
83130588217SMike Christensen 		    explen);
83230588217SMike Christensen 		return;
83330588217SMike Christensen 	}
83430588217SMike Christensen 
83530588217SMike Christensen 	ack = (ds_init_ack_t *)(buf + DS_HDR_SZ);
83630588217SMike Christensen 
83730588217SMike Christensen 	mutex_enter(&port->lock);
83830588217SMike Christensen 
839ffc2bef0SMike Christensen 	if (port->state == DS_PORT_READY) {
840ffc2bef0SMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_ack: port ready" DS_EOL,
841ffc2bef0SMike Christensen 		    PORTID(port));
842ffc2bef0SMike Christensen 		mutex_exit(&port->lock);
843ffc2bef0SMike Christensen 		return;
844ffc2bef0SMike Christensen 	}
845ffc2bef0SMike Christensen 
84630588217SMike Christensen 	if (port->state != DS_PORT_INIT_REQ) {
84730588217SMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_ack: invalid state: %d"
84830588217SMike Christensen 		    DS_EOL, PORTID(port), port->state);
84930588217SMike Christensen 		mutex_exit(&port->lock);
85030588217SMike Christensen 		return;
85130588217SMike Christensen 	}
85230588217SMike Christensen 
85330588217SMike Christensen 	ver = &(ds_vers[port->ver_idx]);
854ffc2bef0SMike Christensen 	major = ver->major;
855ffc2bef0SMike Christensen 	minor = MIN(ver->minor, ack->minor_vers);
85630588217SMike Christensen 	mutex_exit(&port->lock);
85730588217SMike Christensen 
858ffc2bef0SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_ack: port ready v%d.%d" DS_EOL,
859ffc2bef0SMike Christensen 	    PORTID(port), major, minor);
86030588217SMike Christensen 
861ffc2bef0SMike Christensen 	ds_set_port_ready(port, major, minor);
86230588217SMike Christensen }
86330588217SMike Christensen 
86430588217SMike Christensen static void
ds_handle_init_nack(ds_port_t * port,caddr_t buf,size_t len)86530588217SMike Christensen ds_handle_init_nack(ds_port_t *port, caddr_t buf, size_t len)
86630588217SMike Christensen {
86730588217SMike Christensen 	int		idx;
86830588217SMike Christensen 	ds_init_nack_t	*nack;
86930588217SMike Christensen 	ds_ver_t	*ver;
87030588217SMike Christensen 	size_t		explen = DS_MSG_LEN(ds_init_nack_t);
87130588217SMike Christensen 
87230588217SMike Christensen 	/* sanity check the incoming message */
87330588217SMike Christensen 	if (len != explen) {
87430588217SMike Christensen 		DS_DBG_PRCL(CE_WARN, "ds@%lx: <init_nack: invalid message "
87530588217SMike Christensen 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
87630588217SMike Christensen 		    explen);
87730588217SMike Christensen 		return;
87830588217SMike Christensen 	}
87930588217SMike Christensen 
88030588217SMike Christensen 	nack = (ds_init_nack_t *)(buf + DS_HDR_SZ);
88130588217SMike Christensen 
88230588217SMike Christensen 	mutex_enter(&port->lock);
88330588217SMike Christensen 
88430588217SMike Christensen 	if (port->state != DS_PORT_INIT_REQ) {
88530588217SMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_nack: invalid state: %d"
88630588217SMike Christensen 		    DS_EOL, PORTID(port), port->state);
88730588217SMike Christensen 		mutex_exit(&port->lock);
88830588217SMike Christensen 		return;
88930588217SMike Christensen 	}
89030588217SMike Christensen 
89130588217SMike Christensen 	ver = &(ds_vers[port->ver_idx]);
89230588217SMike Christensen 
89330588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_nack: req=v%d.%d, nack=v%d.x"
89430588217SMike Christensen 	    DS_EOL, PORTID(port), ver->major, ver->minor, nack->major_vers);
89530588217SMike Christensen 
89630588217SMike Christensen 	if (nack->major_vers == 0) {
89730588217SMike Christensen 		/* no supported protocol version */
89830588217SMike Christensen 		DS_DBG_PRCL(CE_WARN, "ds@%lx: <init_nack: DS not supported"
89930588217SMike Christensen 		    DS_EOL, PORTID(port));
90030588217SMike Christensen 		mutex_exit(&port->lock);
90130588217SMike Christensen 		return;
90230588217SMike Christensen 	}
90330588217SMike Christensen 
90430588217SMike Christensen 	/*
90530588217SMike Christensen 	 * Walk the version list, looking for a major version
90630588217SMike Christensen 	 * that is as close to the requested major version as
90730588217SMike Christensen 	 * possible.
90830588217SMike Christensen 	 */
90930588217SMike Christensen 	for (idx = port->ver_idx; idx < DS_NUM_VER; idx++) {
91030588217SMike Christensen 		if (ds_vers[idx].major <= nack->major_vers) {
91130588217SMike Christensen 			/* found a version to try */
91230588217SMike Christensen 			goto done;
91330588217SMike Christensen 		}
91430588217SMike Christensen 	}
91530588217SMike Christensen 
91630588217SMike Christensen 	if (idx == DS_NUM_VER) {
91730588217SMike Christensen 		/* no supported version */
91830588217SMike Christensen 		DS_DBG_PRCL(CE_WARN, "ds@%lx: <init_nack: DS v%d.x not "
91930588217SMike Christensen 		    "supported" DS_EOL, PORTID(port), nack->major_vers);
92030588217SMike Christensen 
92130588217SMike Christensen 		mutex_exit(&port->lock);
92230588217SMike Christensen 		return;
92330588217SMike Christensen 	}
92430588217SMike Christensen 
92530588217SMike Christensen done:
92630588217SMike Christensen 	/* start the handshake again */
92730588217SMike Christensen 	port->ver_idx = idx;
92830588217SMike Christensen 	port->state = DS_PORT_LDC_INIT;
92930588217SMike Christensen 	mutex_exit(&port->lock);
93030588217SMike Christensen 
93130588217SMike Christensen 	ds_send_init_req(port);
93230588217SMike Christensen 
93330588217SMike Christensen }
93430588217SMike Christensen 
93530588217SMike Christensen static ds_svc_t *
ds_find_svc_by_id_port(char * svc_id,int is_client,ds_port_t * port)93630588217SMike Christensen ds_find_svc_by_id_port(char *svc_id, int is_client, ds_port_t *port)
93730588217SMike Christensen {
93830588217SMike Christensen 	int		idx;
93930588217SMike Christensen 	ds_svc_t	*svc, *found_svc = 0;
94030588217SMike Christensen 	uint32_t	flag_match = is_client ? DSSF_ISCLIENT : 0;
94130588217SMike Christensen 
94230588217SMike Christensen 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
94330588217SMike Christensen 
94430588217SMike Christensen 	/* walk every table entry */
94530588217SMike Christensen 	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
94630588217SMike Christensen 		svc = ds_svcs.tbl[idx];
94730588217SMike Christensen 		if (DS_SVC_ISFREE(svc))
94830588217SMike Christensen 			continue;
94930588217SMike Christensen 		if (strcmp(svc->cap.svc_id, svc_id) != 0)
95030588217SMike Christensen 			continue;
95130588217SMike Christensen 		if ((svc->flags & DSSF_ISCLIENT) != flag_match)
95230588217SMike Christensen 			continue;
95330588217SMike Christensen 		if (port != NULL && svc->port == port) {
95430588217SMike Christensen 			return (svc);
95530588217SMike Christensen 		} else if (svc->state == DS_SVC_INACTIVE) {
95630588217SMike Christensen 			found_svc = svc;
95730588217SMike Christensen 		} else if (!found_svc) {
95830588217SMike Christensen 			found_svc = svc;
95930588217SMike Christensen 		}
96030588217SMike Christensen 	}
96130588217SMike Christensen 
96230588217SMike Christensen 	return (found_svc);
96330588217SMike Christensen }
96430588217SMike Christensen 
96530588217SMike Christensen static void
ds_handle_reg_req(ds_port_t * port,caddr_t buf,size_t len)96630588217SMike Christensen ds_handle_reg_req(ds_port_t *port, caddr_t buf, size_t len)
96730588217SMike Christensen {
96830588217SMike Christensen 	ds_reg_req_t	*req;
96930588217SMike Christensen 	ds_hdr_t	*hdr;
97030588217SMike Christensen 	ds_reg_ack_t	*ack;
97130588217SMike Christensen 	ds_reg_nack_t	*nack;
97230588217SMike Christensen 	char		*msg;
97330588217SMike Christensen 	size_t		msglen;
97430588217SMike Christensen 	size_t		explen = DS_MSG_LEN(ds_reg_req_t);
97530588217SMike Christensen 	ds_svc_t	*svc = NULL;
97630588217SMike Christensen 	ds_ver_t	version;
97730588217SMike Christensen 	uint16_t	new_major;
97830588217SMike Christensen 	uint16_t	new_minor;
97930588217SMike Christensen 	boolean_t	match;
98030588217SMike Christensen 
98130588217SMike Christensen 	/* sanity check the incoming message */
98230588217SMike Christensen 	if (len < explen) {
98330588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: <reg_req: invalid message "
98430588217SMike Christensen 		    "length (%ld), expected at least %ld" DS_EOL,
98530588217SMike Christensen 		    PORTID(port), len, explen);
98630588217SMike Christensen 		return;
98730588217SMike Christensen 	}
98830588217SMike Christensen 
98930588217SMike Christensen 	req = (ds_reg_req_t *)(buf + DS_HDR_SZ);
99030588217SMike Christensen 
99130588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' ver=%d.%d, hdl=0x%llx"
99230588217SMike Christensen 	    DS_EOL, PORTID(port), req->svc_id, req->major_vers, req->minor_vers,
99330588217SMike Christensen 	    (u_longlong_t)req->svc_handle);
99430588217SMike Christensen 
99530588217SMike Christensen 	mutex_enter(&ds_svcs.lock);
99630588217SMike Christensen 	svc = ds_find_svc_by_id_port(req->svc_id,
99730588217SMike Christensen 	    DS_HDL_ISCLIENT(req->svc_handle) == 0, port);
99830588217SMike Christensen 	if (svc == NULL) {
99930588217SMike Christensen 
100030588217SMike Christensen do_reg_nack:
100130588217SMike Christensen 		mutex_exit(&ds_svcs.lock);
100230588217SMike Christensen 
100330588217SMike Christensen 		msglen = DS_MSG_LEN(ds_reg_nack_t);
100430588217SMike Christensen 		msg = DS_MALLOC(msglen);
100530588217SMike Christensen 
100630588217SMike Christensen 		hdr = (ds_hdr_t *)msg;
100730588217SMike Christensen 		hdr->msg_type = DS_REG_NACK;
100830588217SMike Christensen 		hdr->payload_len = sizeof (ds_reg_nack_t);
100930588217SMike Christensen 
101030588217SMike Christensen 		nack = (ds_reg_nack_t *)(msg + DS_HDR_SZ);
101130588217SMike Christensen 		nack->svc_handle = req->svc_handle;
101230588217SMike Christensen 		nack->result = DS_REG_VER_NACK;
101330588217SMike Christensen 		nack->major_vers = 0;
101430588217SMike Christensen 
101530588217SMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_nack>: '%s'" DS_EOL,
101630588217SMike Christensen 		    PORTID(port), req->svc_id);
101730588217SMike Christensen 		/*
101830588217SMike Christensen 		 * Send the response
101930588217SMike Christensen 		 */
102030588217SMike Christensen 		(void) ds_send_msg(port, msg, msglen);
102130588217SMike Christensen 		DS_FREE(msg, msglen);
102230588217SMike Christensen 		return;
102330588217SMike Christensen 	}
102430588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' found, hdl: 0x%llx" DS_EOL,
102530588217SMike Christensen 	    PORTID(port), req->svc_id, (u_longlong_t)svc->hdl);
102630588217SMike Christensen 
102730588217SMike Christensen 	/*
102830588217SMike Christensen 	 * A client sends out a reg req in order to force service providers to
1029a600f50dSMike Christensen 	 * initiate a reg req from their end (limitation in the protocol).  We
1030a600f50dSMike Christensen 	 * expect the service provider to be in the inactive (DS_SVC_INACTIVE)
1031a600f50dSMike Christensen 	 * state.  If the service provider has already sent out a reg req (the
1032a600f50dSMike Christensen 	 * state is DS_SVC_REG_PENDING) or has already handshaken (the
1033a600f50dSMike Christensen 	 * state is DS_SVC_ACTIVE), then we can simply ignore this reg
1034a600f50dSMike Christensen 	 * req.  For any other state, we force an unregister before initiating
1035a600f50dSMike Christensen 	 * a reg req.
103630588217SMike Christensen 	 */
103730588217SMike Christensen 
103830588217SMike Christensen 	if (DS_HDL_ISCLIENT(req->svc_handle)) {
1039a600f50dSMike Christensen 		switch (svc->state) {
1040a600f50dSMike Christensen 
1041a600f50dSMike Christensen 		case DS_SVC_REG_PENDING:
1042a600f50dSMike Christensen 		case DS_SVC_ACTIVE:
1043a600f50dSMike Christensen 			DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' pinging "
1044a600f50dSMike Christensen 			    "client, state (%x)" DS_EOL, PORTID(port),
1045a600f50dSMike Christensen 			    req->svc_id, svc->state);
1046a600f50dSMike Christensen 			mutex_exit(&ds_svcs.lock);
1047a600f50dSMike Christensen 			return;
1048a600f50dSMike Christensen 
1049a600f50dSMike Christensen 		case DS_SVC_INACTIVE:
1050a600f50dSMike Christensen 			DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' pinging "
1051a600f50dSMike Christensen 			    "client" DS_EOL, PORTID(port), req->svc_id);
1052a600f50dSMike Christensen 			break;
1053a600f50dSMike Christensen 
1054a600f50dSMike Christensen 		default:
1055a600f50dSMike Christensen 			DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' pinging "
1056a600f50dSMike Christensen 			    "client forced unreg, state (%x)" DS_EOL,
1057a600f50dSMike Christensen 			    PORTID(port), req->svc_id, svc->state);
105830588217SMike Christensen 			(void) ds_svc_unregister(svc, port);
1059a600f50dSMike Christensen 			break;
106030588217SMike Christensen 		}
106130588217SMike Christensen 		(void) ds_svc_port_up(svc, port);
106230588217SMike Christensen 		(void) ds_svc_register_onport(svc, port);
106330588217SMike Christensen 		mutex_exit(&ds_svcs.lock);
106430588217SMike Christensen 		return;
106530588217SMike Christensen 	}
106630588217SMike Christensen 
106730588217SMike Christensen 	/*
106830588217SMike Christensen 	 * Only remote service providers can initiate a registration.  The
106930588217SMike Christensen 	 * local sevice from here must be a client service.
107030588217SMike Christensen 	 */
107130588217SMike Christensen 
107230588217SMike Christensen 	match = negotiate_version(svc->cap.nvers, svc->cap.vers,
107330588217SMike Christensen 	    req->major_vers, &new_major, &new_minor);
107430588217SMike Christensen 
107530588217SMike Christensen 	/*
107630588217SMike Christensen 	 * Check version info. ACK only if the major numbers exactly
107730588217SMike Christensen 	 * match. The service entity can retry with a new minor
107830588217SMike Christensen 	 * based on the response sent as part of the NACK.
107930588217SMike Christensen 	 */
108030588217SMike Christensen 	if (match) {
108130588217SMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' svc%d: state: %x "
108230588217SMike Christensen 		    "svc_portid: %d" DS_EOL, PORTID(port), req->svc_id,
108330588217SMike Christensen 		    (int)DS_HDL2IDX(svc->hdl), svc->state,
108430588217SMike Christensen 		    (int)(svc->port == NULL ? -1 : PORTID(svc->port)));
108530588217SMike Christensen 		/*
108630588217SMike Christensen 		 * If the current local service is already in use and
108730588217SMike Christensen 		 * it's not on this port, clone it.
108830588217SMike Christensen 		 */
108930588217SMike Christensen 		if (svc->state != DS_SVC_INACTIVE) {
109030588217SMike Christensen 			if (svc->port != NULL && port == svc->port) {
109130588217SMike Christensen 				/*
109230588217SMike Christensen 				 * Someone probably dropped an unreg req
109330588217SMike Christensen 				 * somewhere.  Force a local unreg.
109430588217SMike Christensen 				 */
109530588217SMike Christensen 				(void) ds_svc_unregister(svc, port);
109630588217SMike Christensen 			} else if (!DS_HDL_ISCLIENT(svc->hdl)) {
109730588217SMike Christensen 				/*
109830588217SMike Christensen 				 * Can't clone a non-client (service provider)
109930588217SMike Christensen 				 * handle.  This is because old in-kernel
110030588217SMike Christensen 				 * service providers can't deal with multiple
110130588217SMike Christensen 				 * handles.
110230588217SMike Christensen 				 */
110330588217SMike Christensen 				goto do_reg_nack;
110430588217SMike Christensen 			} else {
110530588217SMike Christensen 				svc = ds_svc_clone(svc);
110630588217SMike Christensen 			}
110730588217SMike Christensen 		}
110830588217SMike Christensen 		svc->port = port;
110930588217SMike Christensen 		svc->svc_hdl = req->svc_handle;
111030588217SMike Christensen 		svc->state = DS_SVC_ACTIVE;
111130588217SMike Christensen 
111230588217SMike Christensen 		msglen = DS_MSG_LEN(ds_reg_ack_t);
111330588217SMike Christensen 		msg = DS_MALLOC(msglen);
111430588217SMike Christensen 
111530588217SMike Christensen 		hdr = (ds_hdr_t *)msg;
111630588217SMike Christensen 		hdr->msg_type = DS_REG_ACK;
111730588217SMike Christensen 		hdr->payload_len = sizeof (ds_reg_ack_t);
111830588217SMike Christensen 
111930588217SMike Christensen 		ack = (ds_reg_ack_t *)(msg + DS_HDR_SZ);
112030588217SMike Christensen 		ack->svc_handle = req->svc_handle;
112130588217SMike Christensen 		ack->minor_vers = MIN(new_minor, req->minor_vers);
112230588217SMike Christensen 
112330588217SMike Christensen 
112430588217SMike Christensen 		if (svc->ops.ds_reg_cb) {
112530588217SMike Christensen 			/* Call the registration callback */
112630588217SMike Christensen 			version.major = req->major_vers;
112730588217SMike Christensen 			version.minor = ack->minor_vers;
112830588217SMike Christensen 			(*svc->ops.ds_reg_cb)(svc->ops.cb_arg, &version,
112930588217SMike Christensen 			    svc->hdl);
113030588217SMike Christensen 		}
113130588217SMike Christensen 		mutex_exit(&ds_svcs.lock);
113230588217SMike Christensen 
113330588217SMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_ack>: '%s' minor=0x%04X"
113430588217SMike Christensen 		    DS_EOL, PORTID(port), svc->cap.svc_id,
113530588217SMike Christensen 		    MIN(new_minor, req->minor_vers));
113630588217SMike Christensen 	} else {
113730588217SMike Christensen 		mutex_exit(&ds_svcs.lock);
113830588217SMike Christensen 
113930588217SMike Christensen 		msglen = DS_MSG_LEN(ds_reg_nack_t);
114030588217SMike Christensen 		msg = DS_MALLOC(msglen);
114130588217SMike Christensen 
114230588217SMike Christensen 		hdr = (ds_hdr_t *)msg;
114330588217SMike Christensen 		hdr->msg_type = DS_REG_NACK;
114430588217SMike Christensen 		hdr->payload_len = sizeof (ds_reg_nack_t);
114530588217SMike Christensen 
114630588217SMike Christensen 		nack = (ds_reg_nack_t *)(msg + DS_HDR_SZ);
114730588217SMike Christensen 		nack->svc_handle = req->svc_handle;
114830588217SMike Christensen 		nack->result = DS_REG_VER_NACK;
114930588217SMike Christensen 		nack->major_vers = new_major;
115030588217SMike Christensen 
115130588217SMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_nack>: '%s' major=0x%04X"
115230588217SMike Christensen 		    DS_EOL, PORTID(port), svc->cap.svc_id, new_major);
115330588217SMike Christensen 	}
115430588217SMike Christensen 
115530588217SMike Christensen 	/* send message */
115630588217SMike Christensen 	(void) ds_send_msg(port, msg, msglen);
115730588217SMike Christensen 	DS_FREE(msg, msglen);
115830588217SMike Christensen }
115930588217SMike Christensen 
116030588217SMike Christensen static void
ds_handle_reg_ack(ds_port_t * port,caddr_t buf,size_t len)116130588217SMike Christensen ds_handle_reg_ack(ds_port_t *port, caddr_t buf, size_t len)
116230588217SMike Christensen {
116330588217SMike Christensen 	ds_reg_ack_t	*ack;
116430588217SMike Christensen 	ds_ver_t	*ver;
116530588217SMike Christensen 	ds_ver_t	tmpver;
116630588217SMike Christensen 	ds_svc_t	*svc;
116730588217SMike Christensen 	size_t		explen = DS_MSG_LEN(ds_reg_ack_t);
116830588217SMike Christensen 
116930588217SMike Christensen 	/* sanity check the incoming message */
117030588217SMike Christensen 	if (len != explen) {
117130588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: <reg_ack: invalid message "
117230588217SMike Christensen 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
117330588217SMike Christensen 		    explen);
117430588217SMike Christensen 		return;
117530588217SMike Christensen 	}
117630588217SMike Christensen 
117730588217SMike Christensen 	ack = (ds_reg_ack_t *)(buf + DS_HDR_SZ);
117830588217SMike Christensen 
117930588217SMike Christensen 	mutex_enter(&ds_svcs.lock);
118030588217SMike Christensen 
118130588217SMike Christensen 	/*
118230588217SMike Christensen 	 * This searches for service based on how we generate handles
118330588217SMike Christensen 	 * and so only works because this is a reg ack.
118430588217SMike Christensen 	 */
118530588217SMike Christensen 	if (DS_HDL_ISCLIENT(ack->svc_handle) ||
118630588217SMike Christensen 	    (svc = ds_get_svc(ack->svc_handle)) == NULL) {
118730588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: <reg_ack: invalid handle 0x%llx"
118830588217SMike Christensen 		    DS_EOL, PORTID(port), (u_longlong_t)ack->svc_handle);
118930588217SMike Christensen 		goto done;
119030588217SMike Christensen 	}
119130588217SMike Christensen 
119230588217SMike Christensen 	/* make sure the message makes sense */
119330588217SMike Christensen 	if (svc->state != DS_SVC_REG_PENDING) {
119430588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: <reg_ack: invalid state (%d)" DS_EOL,
119530588217SMike Christensen 		    PORTID(port), svc->state);
119630588217SMike Christensen 		goto done;
119730588217SMike Christensen 	}
119830588217SMike Christensen 
119930588217SMike Christensen 	ver = &(svc->cap.vers[svc->ver_idx]);
120030588217SMike Christensen 
120130588217SMike Christensen 	/* major version has been agreed upon */
120230588217SMike Christensen 	svc->ver.major = ver->major;
120330588217SMike Christensen 
120430588217SMike Christensen 	if (ack->minor_vers >= ver->minor) {
120530588217SMike Christensen 		/*
120630588217SMike Christensen 		 * Use the minor version specified in the
120730588217SMike Christensen 		 * original request.
120830588217SMike Christensen 		 */
120930588217SMike Christensen 		svc->ver.minor = ver->minor;
121030588217SMike Christensen 	} else {
121130588217SMike Christensen 		/*
121230588217SMike Christensen 		 * Use the lower minor version returned in
121330588217SMike Christensen 		 * the ack. By defninition, all lower minor
121430588217SMike Christensen 		 * versions must be supported.
121530588217SMike Christensen 		 */
121630588217SMike Christensen 		svc->ver.minor = ack->minor_vers;
121730588217SMike Christensen 	}
121830588217SMike Christensen 
121930588217SMike Christensen 	svc->state = DS_SVC_ACTIVE;
122030588217SMike Christensen 	svc->port = port;
122130588217SMike Christensen 
122230588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_ack: '%s' v%d.%d ready, hdl=0x%llx"
122330588217SMike Christensen 	    DS_EOL, PORTID(port), svc->cap.svc_id, svc->ver.major,
122430588217SMike Christensen 	    svc->ver.minor, (u_longlong_t)svc->hdl);
122530588217SMike Christensen 
122630588217SMike Christensen 	/* notify the client that registration is complete */
122730588217SMike Christensen 	if (svc->ops.ds_reg_cb) {
122830588217SMike Christensen 		/*
122930588217SMike Christensen 		 * Use a temporary version structure so that
123030588217SMike Christensen 		 * the copy in the svc structure cannot be
123130588217SMike Christensen 		 * modified by the client.
123230588217SMike Christensen 		 */
123330588217SMike Christensen 		tmpver.major = svc->ver.major;
123430588217SMike Christensen 		tmpver.minor = svc->ver.minor;
123530588217SMike Christensen 
123630588217SMike Christensen 		(*svc->ops.ds_reg_cb)(svc->ops.cb_arg, &tmpver, svc->hdl);
123730588217SMike Christensen 	}
123830588217SMike Christensen 
123930588217SMike Christensen done:
124030588217SMike Christensen 	mutex_exit(&ds_svcs.lock);
124130588217SMike Christensen }
124230588217SMike Christensen 
1243e9406929SMike Christensen static boolean_t
ds_port_is_ready(ds_port_t * port)1244e9406929SMike Christensen ds_port_is_ready(ds_port_t *port)
1245e9406929SMike Christensen {
1246e9406929SMike Christensen 	boolean_t is_ready;
1247e9406929SMike Christensen 
1248e9406929SMike Christensen 	mutex_enter(&port->lock);
1249e9406929SMike Christensen 	is_ready = (port->ldc.state == LDC_UP) &&
1250e9406929SMike Christensen 	    (port->state == DS_PORT_READY);
1251e9406929SMike Christensen 	mutex_exit(&port->lock);
1252e9406929SMike Christensen 	return (is_ready);
1253e9406929SMike Christensen }
1254e9406929SMike Christensen 
125530588217SMike Christensen static void
ds_try_next_port(ds_svc_t * svc,int portid)125630588217SMike Christensen ds_try_next_port(ds_svc_t *svc, int portid)
125730588217SMike Christensen {
125830588217SMike Christensen 	ds_port_t *port;
125930588217SMike Christensen 	ds_portset_t totry;
126030588217SMike Christensen 	int i;
126130588217SMike Christensen 
126230588217SMike Christensen 	DS_DBG_LDC(CE_NOTE, "ds@%x %s" DS_EOL, portid, __func__);
12633ef557bfSMike Christensen 
12643ef557bfSMike Christensen 	/*
12653ef557bfSMike Christensen 	 * Get the ports that haven't been tried yet and are available to try.
12663ef557bfSMike Christensen 	 */
1267ffc2bef0SMike Christensen 	DS_PORTSET_DUP(totry, svc->avail);
12683ef557bfSMike Christensen 	for (i = 0; i < DS_MAX_PORTS; i++) {
1269ffc2bef0SMike Christensen 		if (DS_PORT_IN_SET(svc->tried, i))
1270ffc2bef0SMike Christensen 			DS_PORTSET_DEL(totry, i);
12713ef557bfSMike Christensen 	}
12723ef557bfSMike Christensen 
127330588217SMike Christensen 	if (DS_PORTSET_ISNULL(totry))
127430588217SMike Christensen 		return;
127530588217SMike Christensen 
127630588217SMike Christensen 	for (i = 0; i < DS_MAX_PORTS; i++, portid++) {
127730588217SMike Christensen 		if (portid >= DS_MAX_PORTS) {
127830588217SMike Christensen 			portid = 0;
127930588217SMike Christensen 		}
128030588217SMike Christensen 
128130588217SMike Christensen 		/*
128230588217SMike Christensen 		 * If the port is not in the available list,
128330588217SMike Christensen 		 * it is not a candidate for registration.
128430588217SMike Christensen 		 */
128530588217SMike Christensen 		if (!DS_PORT_IN_SET(totry, portid)) {
128630588217SMike Christensen 			continue;
128730588217SMike Christensen 		}
128830588217SMike Christensen 
128930588217SMike Christensen 		port = &ds_ports[portid];
1290e9406929SMike Christensen 
1291e9406929SMike Christensen 		if (!ds_port_is_ready(port))
1292e9406929SMike Christensen 			continue;
1293e9406929SMike Christensen 
129430588217SMike Christensen 		DS_DBG_LDC(CE_NOTE, "ds@%x: %s trying ldc.id: %d" DS_EOL,
129530588217SMike Christensen 		    portid, __func__, (uint_t)(port->ldc.id));
1296e9406929SMike Christensen 
1297e9406929SMike Christensen 		DS_PORTSET_ADD(svc->tried, portid);
1298e9406929SMike Christensen 
129930588217SMike Christensen 		if (ds_send_reg_req(svc, port) == 0) {
130030588217SMike Christensen 			DS_DBG_LDC(CE_NOTE, "ds@%x: %s reg msg send OK" DS_EOL,
130130588217SMike Christensen 			    portid, __func__);
130230588217SMike Christensen 			/* register sent successfully */
130330588217SMike Christensen 			break;
130430588217SMike Christensen 		}
130530588217SMike Christensen 		DS_DBG_LDC(CE_NOTE, "ds@%x: %s reg msg send FAIL" DS_EOL,
130630588217SMike Christensen 		    portid, __func__);
130730588217SMike Christensen 
130830588217SMike Christensen 		/* reset the service to try the next port */
130930588217SMike Christensen 		ds_reset_svc(svc, port);
131030588217SMike Christensen 	}
131130588217SMike Christensen }
131230588217SMike Christensen 
131330588217SMike Christensen static void
ds_handle_reg_nack(ds_port_t * port,caddr_t buf,size_t len)131430588217SMike Christensen ds_handle_reg_nack(ds_port_t *port, caddr_t buf, size_t len)
131530588217SMike Christensen {
131630588217SMike Christensen 	ds_reg_nack_t	*nack;
131730588217SMike Christensen 	ds_svc_t	*svc;
131830588217SMike Christensen 	int		idx;
131930588217SMike Christensen 	size_t		explen = DS_MSG_LEN(ds_reg_nack_t);
132030588217SMike Christensen 
132130588217SMike Christensen 	/* sanity check the incoming message */
132230588217SMike Christensen 	if (len != explen) {
132330588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: <reg_nack: invalid message "
132430588217SMike Christensen 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
132530588217SMike Christensen 		    explen);
132630588217SMike Christensen 		return;
132730588217SMike Christensen 	}
132830588217SMike Christensen 
132930588217SMike Christensen 	nack = (ds_reg_nack_t *)(buf + DS_HDR_SZ);
133030588217SMike Christensen 
133130588217SMike Christensen 	mutex_enter(&ds_svcs.lock);
133230588217SMike Christensen 
133330588217SMike Christensen 	/*
133430588217SMike Christensen 	 * We expect a reg_nack for a client ping.
133530588217SMike Christensen 	 */
133630588217SMike Christensen 	if (DS_HDL_ISCLIENT(nack->svc_handle)) {
133730588217SMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: ping hdl: 0x%llx"
133830588217SMike Christensen 		    DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle);
133930588217SMike Christensen 		goto done;
134030588217SMike Christensen 	}
134130588217SMike Christensen 
134230588217SMike Christensen 	/*
134330588217SMike Christensen 	 * This searches for service based on how we generate handles
134430588217SMike Christensen 	 * and so only works because this is a reg nack.
134530588217SMike Christensen 	 */
134630588217SMike Christensen 	if ((svc = ds_get_svc(nack->svc_handle)) == NULL) {
134730588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: <reg_nack: invalid handle 0x%llx"
134830588217SMike Christensen 		    DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle);
134930588217SMike Christensen 		goto done;
135030588217SMike Christensen 	}
135130588217SMike Christensen 
135230588217SMike Christensen 	/* make sure the message makes sense */
135330588217SMike Christensen 	if (svc->state != DS_SVC_REG_PENDING) {
1354ffc2bef0SMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: '%s' handle: 0x%llx "
1355ffc2bef0SMike Christensen 		    "invalid state (%d)" DS_EOL, PORTID(port), svc->cap.svc_id,
1356ffc2bef0SMike Christensen 		    (u_longlong_t)nack->svc_handle, svc->state);
135730588217SMike Christensen 		goto done;
135830588217SMike Christensen 	}
135930588217SMike Christensen 
136030588217SMike Christensen 	if (nack->result == DS_REG_DUP) {
136130588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: <reg_nack: duplicate registration "
136230588217SMike Christensen 		    " for %s" DS_EOL, PORTID(port), svc->cap.svc_id);
136330588217SMike Christensen 		ds_reset_svc(svc, port);
136430588217SMike Christensen 		goto done;
136530588217SMike Christensen 	}
136630588217SMike Christensen 
136730588217SMike Christensen 	/*
136830588217SMike Christensen 	 * A major version of zero indicates that the
136930588217SMike Christensen 	 * service is not supported at all.
137030588217SMike Christensen 	 */
137130588217SMike Christensen 	if (nack->major_vers == 0) {
137230588217SMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: '%s' not supported"
137330588217SMike Christensen 		    DS_EOL, PORTID(port), svc->cap.svc_id);
137430588217SMike Christensen 		ds_reset_svc(svc, port);
137530588217SMike Christensen 		if ((svc->flags & DSSF_ISCLIENT) == 0)
137630588217SMike Christensen 			ds_try_next_port(svc, PORTID(port) + 1);
137730588217SMike Christensen 		goto done;
137830588217SMike Christensen 	}
137930588217SMike Christensen 
138030588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: '%s' hdl=0x%llx, nack=%d.x"
138130588217SMike Christensen 	    DS_EOL, PORTID(port), svc->cap.svc_id,
138230588217SMike Christensen 	    (u_longlong_t)nack->svc_handle, nack->major_vers);
138330588217SMike Christensen 
138430588217SMike Christensen 	/*
138530588217SMike Christensen 	 * Walk the version list for the service, looking for
138630588217SMike Christensen 	 * a major version that is as close to the requested
138730588217SMike Christensen 	 * major version as possible.
138830588217SMike Christensen 	 */
138930588217SMike Christensen 	for (idx = svc->ver_idx; idx < svc->cap.nvers; idx++) {
139030588217SMike Christensen 		if (svc->cap.vers[idx].major <= nack->major_vers) {
139130588217SMike Christensen 			/* found a version to try */
139230588217SMike Christensen 			break;
139330588217SMike Christensen 		}
139430588217SMike Christensen 	}
139530588217SMike Christensen 
139630588217SMike Christensen 	if (idx == svc->cap.nvers) {
139730588217SMike Christensen 		/* no supported version */
139830588217SMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: %s v%d.x not supported"
139930588217SMike Christensen 		    DS_EOL, PORTID(port), svc->cap.svc_id, nack->major_vers);
140030588217SMike Christensen 		ds_reset_svc(svc, port);
140130588217SMike Christensen 		if ((svc->flags & DSSF_ISCLIENT) == 0)
140230588217SMike Christensen 			ds_try_next_port(svc, PORTID(port) + 1);
140330588217SMike Christensen 		goto done;
140430588217SMike Christensen 	}
140530588217SMike Christensen 
140630588217SMike Christensen 	/* start the handshake again */
140730588217SMike Christensen 	svc->state = DS_SVC_INACTIVE;
140830588217SMike Christensen 	svc->ver_idx = idx;
140930588217SMike Christensen 
141030588217SMike Christensen 	(void) ds_svc_register(svc, NULL);
141130588217SMike Christensen 
141230588217SMike Christensen done:
141330588217SMike Christensen 	mutex_exit(&ds_svcs.lock);
141430588217SMike Christensen }
141530588217SMike Christensen 
141630588217SMike Christensen static void
ds_handle_unreg_req(ds_port_t * port,caddr_t buf,size_t len)141730588217SMike Christensen ds_handle_unreg_req(ds_port_t *port, caddr_t buf, size_t len)
141830588217SMike Christensen {
141930588217SMike Christensen 	ds_hdr_t	*hdr;
142030588217SMike Christensen 	ds_unreg_req_t	*req;
142130588217SMike Christensen 	ds_unreg_ack_t	*ack;
142230588217SMike Christensen 	ds_svc_t	*svc;
142330588217SMike Christensen 	char		*msg;
142430588217SMike Christensen 	size_t		msglen;
142530588217SMike Christensen 	size_t		explen = DS_MSG_LEN(ds_unreg_req_t);
1426beba1dd8SMike Christensen 	boolean_t	is_up;
142730588217SMike Christensen 
142830588217SMike Christensen 	/* sanity check the incoming message */
142930588217SMike Christensen 	if (len != explen) {
143030588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: <unreg_req: invalid message "
143130588217SMike Christensen 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
143230588217SMike Christensen 		    explen);
143330588217SMike Christensen 		return;
143430588217SMike Christensen 	}
143530588217SMike Christensen 
143630588217SMike Christensen 	req = (ds_unreg_req_t *)(buf + DS_HDR_SZ);
143730588217SMike Christensen 
143830588217SMike Christensen 	mutex_enter(&ds_svcs.lock);
143930588217SMike Christensen 
144030588217SMike Christensen 	/* lookup appropriate client or service */
144130588217SMike Christensen 	if (DS_HDL_ISCLIENT(req->svc_handle) ||
144230588217SMike Christensen 	    ((svc = ds_find_clnt_svc_by_hdl_port(req->svc_handle, port))
144330588217SMike Christensen 	    == NULL && ((svc = ds_get_svc(req->svc_handle)) == NULL ||
144430588217SMike Christensen 	    svc->port != port))) {
1445beba1dd8SMike Christensen 		mutex_exit(&ds_svcs.lock);
1446beba1dd8SMike Christensen 		mutex_enter(&port->lock);
1447beba1dd8SMike Christensen 		is_up = (port->ldc.state == LDC_UP);
1448beba1dd8SMike Christensen 		mutex_exit(&port->lock);
1449beba1dd8SMike Christensen 		if (!is_up)
1450beba1dd8SMike Christensen 			return;
1451ffc2bef0SMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_req: invalid handle 0x%llx"
145230588217SMike Christensen 		    DS_EOL, PORTID(port), (u_longlong_t)req->svc_handle);
145330588217SMike Christensen 		ds_send_unreg_nack(port, req->svc_handle);
145430588217SMike Christensen 		return;
145530588217SMike Christensen 	}
145630588217SMike Christensen 
145730588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_req: '%s' handle 0x%llx" DS_EOL,
145830588217SMike Christensen 	    PORTID(port), svc->cap.svc_id, (u_longlong_t)req->svc_handle);
145930588217SMike Christensen 
146030588217SMike Christensen 	(void) ds_svc_unregister(svc, svc->port);
146130588217SMike Christensen 
146230588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: unreg_ack>: '%s' hdl=0x%llx" DS_EOL,
146330588217SMike Christensen 	    PORTID(port), svc->cap.svc_id, (u_longlong_t)req->svc_handle);
146430588217SMike Christensen 
146530588217SMike Christensen 	ds_check_for_dup_services(svc);
146630588217SMike Christensen 
146730588217SMike Christensen 	mutex_exit(&ds_svcs.lock);
146830588217SMike Christensen 
146930588217SMike Christensen 	msglen = DS_HDR_SZ + sizeof (ds_unreg_ack_t);
147030588217SMike Christensen 	msg = DS_MALLOC(msglen);
147130588217SMike Christensen 
147230588217SMike Christensen 	hdr = (ds_hdr_t *)msg;
147330588217SMike Christensen 	hdr->msg_type = DS_UNREG_ACK;
147430588217SMike Christensen 	hdr->payload_len = sizeof (ds_unreg_ack_t);
147530588217SMike Christensen 
147630588217SMike Christensen 	ack = (ds_unreg_ack_t *)(msg + DS_HDR_SZ);
147730588217SMike Christensen 	ack->svc_handle = req->svc_handle;
147830588217SMike Christensen 
147930588217SMike Christensen 	/* send message */
148030588217SMike Christensen 	(void) ds_send_msg(port, msg, msglen);
148130588217SMike Christensen 	DS_FREE(msg, msglen);
148230588217SMike Christensen 
148330588217SMike Christensen }
148430588217SMike Christensen 
148530588217SMike Christensen static void
ds_handle_unreg_ack(ds_port_t * port,caddr_t buf,size_t len)148630588217SMike Christensen ds_handle_unreg_ack(ds_port_t *port, caddr_t buf, size_t len)
148730588217SMike Christensen {
148830588217SMike Christensen 	ds_unreg_ack_t	*ack;
148930588217SMike Christensen 	size_t		explen = DS_MSG_LEN(ds_unreg_ack_t);
149030588217SMike Christensen 
149130588217SMike Christensen 	/* sanity check the incoming message */
149230588217SMike Christensen 	if (len != explen) {
149330588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: <unreg_ack: invalid message "
149430588217SMike Christensen 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
149530588217SMike Christensen 		    explen);
149630588217SMike Christensen 		return;
149730588217SMike Christensen 	}
149830588217SMike Christensen 
149930588217SMike Christensen 	ack = (ds_unreg_ack_t *)(buf + DS_HDR_SZ);
150030588217SMike Christensen 
150130588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_ack: hdl=0x%llx" DS_EOL,
150230588217SMike Christensen 	    PORTID(port), (u_longlong_t)ack->svc_handle);
150330588217SMike Christensen 
1504b1ad746cSMike Christensen #ifdef DEBUG
150530588217SMike Christensen 	mutex_enter(&ds_svcs.lock);
150630588217SMike Christensen 
150730588217SMike Christensen 	/*
150830588217SMike Christensen 	 * Since the unregister request was initiated locally,
150930588217SMike Christensen 	 * the service structure has already been torn down.
151030588217SMike Christensen 	 * Just perform a sanity check to make sure the message
151130588217SMike Christensen 	 * is appropriate.
151230588217SMike Christensen 	 */
151330588217SMike Christensen 	if (ds_get_svc(ack->svc_handle) != NULL) {
1514b1ad746cSMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_ack: handle 0x%llx in use"
151530588217SMike Christensen 		    DS_EOL, PORTID(port), (u_longlong_t)ack->svc_handle);
151630588217SMike Christensen 	}
151730588217SMike Christensen 
151830588217SMike Christensen 	mutex_exit(&ds_svcs.lock);
1519b1ad746cSMike Christensen #endif	/* DEBUG */
152030588217SMike Christensen }
152130588217SMike Christensen 
152230588217SMike Christensen static void
ds_handle_unreg_nack(ds_port_t * port,caddr_t buf,size_t len)152330588217SMike Christensen ds_handle_unreg_nack(ds_port_t *port, caddr_t buf, size_t len)
152430588217SMike Christensen {
152530588217SMike Christensen 	ds_unreg_nack_t	*nack;
152630588217SMike Christensen 	size_t		explen = DS_MSG_LEN(ds_unreg_nack_t);
152730588217SMike Christensen 
152830588217SMike Christensen 	/* sanity check the incoming message */
152930588217SMike Christensen 	if (len != explen) {
153030588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: <unreg_nack: invalid message "
153130588217SMike Christensen 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
153230588217SMike Christensen 		    explen);
153330588217SMike Christensen 		return;
153430588217SMike Christensen 	}
153530588217SMike Christensen 
153630588217SMike Christensen 	nack = (ds_unreg_nack_t *)(buf + DS_HDR_SZ);
153730588217SMike Christensen 
153830588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_nack: hdl=0x%llx" DS_EOL,
153930588217SMike Christensen 	    PORTID(port), (u_longlong_t)nack->svc_handle);
154030588217SMike Christensen 
1541b1ad746cSMike Christensen #ifdef DEBUG
154230588217SMike Christensen 	mutex_enter(&ds_svcs.lock);
154330588217SMike Christensen 
154430588217SMike Christensen 	/*
154530588217SMike Christensen 	 * Since the unregister request was initiated locally,
154630588217SMike Christensen 	 * the service structure has already been torn down.
154730588217SMike Christensen 	 * Just perform a sanity check to make sure the message
154830588217SMike Christensen 	 * is appropriate.
154930588217SMike Christensen 	 */
155030588217SMike Christensen 	if (ds_get_svc(nack->svc_handle) != NULL) {
1551b1ad746cSMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_nack: handle 0x%llx in use"
155230588217SMike Christensen 		    DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle);
155330588217SMike Christensen 	}
155430588217SMike Christensen 
155530588217SMike Christensen 	mutex_exit(&ds_svcs.lock);
1556b1ad746cSMike Christensen #endif	/* DEBUG */
155730588217SMike Christensen }
155830588217SMike Christensen 
155930588217SMike Christensen static void
ds_handle_data(ds_port_t * port,caddr_t buf,size_t len)156030588217SMike Christensen ds_handle_data(ds_port_t *port, caddr_t buf, size_t len)
156130588217SMike Christensen {
156230588217SMike Christensen 	ds_data_handle_t	*data;
156330588217SMike Christensen 	ds_svc_t		*svc;
156430588217SMike Christensen 	char			*msg;
156530588217SMike Christensen 	int			msgsz;
156630588217SMike Christensen 	int			hdrsz;
156730588217SMike Christensen 	size_t			explen = DS_MSG_LEN(ds_data_handle_t);
156830588217SMike Christensen 
156930588217SMike Christensen 	/* sanity check the incoming message */
157030588217SMike Christensen 	if (len < explen) {
157130588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: <data: invalid message length "
157230588217SMike Christensen 		    "(%ld), expected at least %ld" DS_EOL, PORTID(port), len,
157330588217SMike Christensen 		    explen);
157430588217SMike Christensen 		return;
157530588217SMike Christensen 	}
157630588217SMike Christensen 
157730588217SMike Christensen 	data = (ds_data_handle_t *)(buf + DS_HDR_SZ);
157830588217SMike Christensen 
157930588217SMike Christensen 	hdrsz = DS_HDR_SZ + sizeof (ds_data_handle_t);
158030588217SMike Christensen 	msgsz = len - hdrsz;
158130588217SMike Christensen 
158230588217SMike Christensen 	/* strip off the header for the client */
158330588217SMike Christensen 	msg = (msgsz) ? (buf + hdrsz) : NULL;
158430588217SMike Christensen 
158530588217SMike Christensen 	mutex_enter(&ds_svcs.lock);
158630588217SMike Christensen 
158730588217SMike Christensen 	if ((svc = ds_find_clnt_svc_by_hdl_port(data->svc_handle, port))
158830588217SMike Christensen 	    == NULL) {
158930588217SMike Christensen 		if ((svc = ds_get_svc(data->svc_handle)) == NULL) {
159030588217SMike Christensen 			mutex_exit(&ds_svcs.lock);
159130588217SMike Christensen 			cmn_err(CE_WARN, "ds@%lx: <data: invalid handle 0x%llx"
159230588217SMike Christensen 			    DS_EOL, PORTID(port),
159330588217SMike Christensen 			    (u_longlong_t)data->svc_handle);
159430588217SMike Christensen 			ds_send_data_nack(port, data->svc_handle);
159530588217SMike Christensen 			return;
159630588217SMike Christensen 		}
159730588217SMike Christensen 	}
159830588217SMike Christensen 
159930588217SMike Christensen 	mutex_exit(&ds_svcs.lock);
160030588217SMike Christensen 
160130588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <data: '%s' hdl=0x%llx" DS_EOL,
160230588217SMike Christensen 	    PORTID(port), svc->cap.svc_id, (u_longlong_t)svc->hdl);
160330588217SMike Christensen 	DS_DUMP_MSG(DS_DBG_FLAG_PRCL, msg, msgsz);
160430588217SMike Christensen 
160530588217SMike Christensen 	/* dispatch this message to the client */
160630588217SMike Christensen 	(*svc->ops.ds_data_cb)(svc->ops.cb_arg, msg, msgsz);
160730588217SMike Christensen }
160830588217SMike Christensen 
160930588217SMike Christensen static void
ds_handle_nack(ds_port_t * port,caddr_t buf,size_t len)161030588217SMike Christensen ds_handle_nack(ds_port_t *port, caddr_t buf, size_t len)
161130588217SMike Christensen {
161230588217SMike Christensen 	ds_svc_t	*svc;
161330588217SMike Christensen 	ds_data_nack_t	*nack;
161430588217SMike Christensen 	size_t		explen = DS_MSG_LEN(ds_data_nack_t);
161530588217SMike Christensen 
161630588217SMike Christensen 	/* sanity check the incoming message */
161730588217SMike Christensen 	if (len != explen) {
161830588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: <data_nack: invalid message "
161930588217SMike Christensen 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
162030588217SMike Christensen 		    explen);
162130588217SMike Christensen 		return;
162230588217SMike Christensen 	}
162330588217SMike Christensen 
162430588217SMike Christensen 	nack = (ds_data_nack_t *)(buf + DS_HDR_SZ);
162530588217SMike Christensen 
162630588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: data_nack: hdl=0x%llx, result=0x%llx"
162730588217SMike Christensen 	    DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle,
162830588217SMike Christensen 	    (u_longlong_t)nack->result);
162930588217SMike Christensen 
163030588217SMike Christensen 	if (nack->result == DS_INV_HDL) {
163130588217SMike Christensen 
163230588217SMike Christensen 		mutex_enter(&ds_svcs.lock);
163330588217SMike Christensen 
163430588217SMike Christensen 		if ((svc = ds_find_clnt_svc_by_hdl_port(nack->svc_handle,
163530588217SMike Christensen 		    port)) == NULL) {
163630588217SMike Christensen 			if ((svc = ds_get_svc(nack->svc_handle)) == NULL) {
163730588217SMike Christensen 				mutex_exit(&ds_svcs.lock);
163830588217SMike Christensen 				return;
163930588217SMike Christensen 			}
164030588217SMike Christensen 		}
164130588217SMike Christensen 
164230588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: <data_nack: handle 0x%llx reported "
164330588217SMike Christensen 		    " as invalid" DS_EOL, PORTID(port),
164430588217SMike Christensen 		    (u_longlong_t)nack->svc_handle);
164530588217SMike Christensen 
164630588217SMike Christensen 		(void) ds_svc_unregister(svc, svc->port);
164730588217SMike Christensen 
164830588217SMike Christensen 		mutex_exit(&ds_svcs.lock);
164930588217SMike Christensen 	}
165030588217SMike Christensen }
165130588217SMike Christensen 
165230588217SMike Christensen /* Initialize the port */
165330588217SMike Christensen void
ds_send_init_req(ds_port_t * port)165430588217SMike Christensen ds_send_init_req(ds_port_t *port)
165530588217SMike Christensen {
165630588217SMike Christensen 	ds_hdr_t	*hdr;
165730588217SMike Christensen 	ds_init_req_t	*init_req;
165830588217SMike Christensen 	size_t		msglen;
165930588217SMike Christensen 	ds_ver_t	*vers = &ds_vers[port->ver_idx];
166030588217SMike Christensen 
166130588217SMike Christensen 	mutex_enter(&port->lock);
166230588217SMike Christensen 	if (port->state != DS_PORT_LDC_INIT) {
166330588217SMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_req>: invalid state: %d"
166430588217SMike Christensen 		    DS_EOL, PORTID(port), port->state);
166530588217SMike Christensen 		mutex_exit(&port->lock);
166630588217SMike Christensen 		return;
166730588217SMike Christensen 	}
166830588217SMike Christensen 	mutex_exit(&port->lock);
166930588217SMike Christensen 
167030588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_req>: req=v%d.%d" DS_EOL,
167130588217SMike Christensen 	    PORTID(port), vers->major, vers->minor);
167230588217SMike Christensen 
167330588217SMike Christensen 	msglen = DS_HDR_SZ + sizeof (ds_init_req_t);
167430588217SMike Christensen 	hdr = DS_MALLOC(msglen);
167530588217SMike Christensen 
167630588217SMike Christensen 	hdr->msg_type = DS_INIT_REQ;
167730588217SMike Christensen 	hdr->payload_len = sizeof (ds_init_req_t);
167830588217SMike Christensen 
167930588217SMike Christensen 	init_req = (ds_init_req_t *)((caddr_t)hdr + DS_HDR_SZ);
168030588217SMike Christensen 	init_req->major_vers = vers->major;
168130588217SMike Christensen 	init_req->minor_vers = vers->minor;
168230588217SMike Christensen 
168330588217SMike Christensen 	if (ds_send_msg(port, (caddr_t)hdr, msglen) == 0) {
168430588217SMike Christensen 		/*
168530588217SMike Christensen 		 * We've left the port state unlocked over the malloc/send,
168630588217SMike Christensen 		 * make sure no one has changed the state under us before
168730588217SMike Christensen 		 * we update the state.
168830588217SMike Christensen 		 */
168930588217SMike Christensen 		mutex_enter(&port->lock);
169030588217SMike Christensen 		if (port->state == DS_PORT_LDC_INIT)
169130588217SMike Christensen 			port->state = DS_PORT_INIT_REQ;
169230588217SMike Christensen 		mutex_exit(&port->lock);
169330588217SMike Christensen 	}
169430588217SMike Christensen 	DS_FREE(hdr, msglen);
169530588217SMike Christensen }
169630588217SMike Christensen 
169730588217SMike Christensen static int
ds_send_reg_req(ds_svc_t * svc,ds_port_t * port)169830588217SMike Christensen ds_send_reg_req(ds_svc_t *svc, ds_port_t *port)
169930588217SMike Christensen {
170030588217SMike Christensen 	ds_ver_t	*ver;
170130588217SMike Christensen 	ds_hdr_t	*hdr;
170230588217SMike Christensen 	caddr_t		msg;
170330588217SMike Christensen 	size_t		msglen;
170430588217SMike Christensen 	ds_reg_req_t	*req;
170530588217SMike Christensen 	size_t		idlen;
170630588217SMike Christensen 	int		rv;
170730588217SMike Christensen 
1708a600f50dSMike Christensen 	if ((svc->state != DS_SVC_INACTIVE) &&
1709a600f50dSMike Christensen 	    ((svc->flags & DSSF_ISCLIENT) == 0)) {
1710a600f50dSMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_req>: invalid svc state (%d) "
1711a600f50dSMike Christensen 		    "for svc '%s'" DS_EOL, PORTID(port), svc->state,
1712a600f50dSMike Christensen 		    svc->cap.svc_id);
1713a600f50dSMike Christensen 		return (-1);
1714a600f50dSMike Christensen 	}
171530588217SMike Christensen 
171630588217SMike Christensen 	mutex_enter(&port->lock);
171730588217SMike Christensen 
171830588217SMike Christensen 	/* check on the LDC to Zeus */
171930588217SMike Christensen 	if (port->ldc.state != LDC_UP) {
172030588217SMike Christensen 		/* can not send message */
172130588217SMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_req>: channel %ld is not up"
172230588217SMike Christensen 		    DS_EOL, PORTID(port), port->ldc.id);
172330588217SMike Christensen 		mutex_exit(&port->lock);
172430588217SMike Christensen 		return (-1);
172530588217SMike Christensen 	}
172630588217SMike Christensen 
172730588217SMike Christensen 	/* make sure port is ready */
172830588217SMike Christensen 	if (port->state != DS_PORT_READY) {
172930588217SMike Christensen 		/* can not send message */
173030588217SMike Christensen 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_req>: port is not ready"
173130588217SMike Christensen 		    DS_EOL, PORTID(port));
173230588217SMike Christensen 		mutex_exit(&port->lock);
173330588217SMike Christensen 		return (-1);
173430588217SMike Christensen 	}
173530588217SMike Christensen 
173630588217SMike Christensen 	mutex_exit(&port->lock);
173730588217SMike Christensen 
173830588217SMike Christensen 	/* allocate the message buffer */
173930588217SMike Christensen 	idlen = strlen(svc->cap.svc_id);
174030588217SMike Christensen 	msglen = DS_HDR_SZ + sizeof (ds_reg_req_t) + idlen;
174130588217SMike Christensen 	msg = DS_MALLOC(msglen);
174230588217SMike Christensen 
174330588217SMike Christensen 	/* copy in the header data */
174430588217SMike Christensen 	hdr = (ds_hdr_t *)msg;
174530588217SMike Christensen 	hdr->msg_type = DS_REG_REQ;
174630588217SMike Christensen 	hdr->payload_len = sizeof (ds_reg_req_t) + idlen;
174730588217SMike Christensen 
174830588217SMike Christensen 	req = (ds_reg_req_t *)(msg + DS_HDR_SZ);
174930588217SMike Christensen 	req->svc_handle = svc->hdl;
175030588217SMike Christensen 	ver = &(svc->cap.vers[svc->ver_idx]);
175130588217SMike Christensen 	req->major_vers = ver->major;
175230588217SMike Christensen 	req->minor_vers = ver->minor;
175330588217SMike Christensen 
175430588217SMike Christensen 	/* copy in the service id */
175530588217SMike Christensen 	(void) memcpy(req->svc_id, svc->cap.svc_id, idlen + 1);
175630588217SMike Christensen 
175730588217SMike Christensen 	/* send the message */
175830588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_req>: '%s' ver=%d.%d, hdl=0x%llx"
175930588217SMike Christensen 	    DS_EOL, PORTID(port), svc->cap.svc_id, ver->major, ver->minor,
176030588217SMike Christensen 	    (u_longlong_t)svc->hdl);
176130588217SMike Christensen 
176230588217SMike Christensen 	if ((rv = ds_send_msg(port, msg, msglen)) != 0) {
176330588217SMike Christensen 		svc->port = port;
176430588217SMike Christensen 		rv = -1;
176530588217SMike Christensen 	} else if ((svc->flags & DSSF_ISCLIENT) == 0) {
176630588217SMike Christensen 		svc->state = DS_SVC_REG_PENDING;
176730588217SMike Christensen 	}
176830588217SMike Christensen 	DS_FREE(msg, msglen);
176930588217SMike Christensen 
177030588217SMike Christensen 	return (rv);
177130588217SMike Christensen }
177230588217SMike Christensen 
177330588217SMike Christensen /*
177430588217SMike Christensen  * Keep around in case we want this later
177530588217SMike Christensen  */
177630588217SMike Christensen int
ds_send_unreg_req(ds_svc_t * svc)177730588217SMike Christensen ds_send_unreg_req(ds_svc_t *svc)
177830588217SMike Christensen {
177930588217SMike Christensen 	caddr_t		msg;
178030588217SMike Christensen 	size_t		msglen;
178130588217SMike Christensen 	ds_hdr_t	*hdr;
178230588217SMike Christensen 	ds_unreg_req_t	*req;
178330588217SMike Christensen 	ds_port_t	*port = svc->port;
178430588217SMike Christensen 	int		rv;
178530588217SMike Christensen 
178630588217SMike Christensen 	if (port == NULL) {
178730588217SMike Christensen 		DS_DBG(CE_NOTE, "send_unreg_req: service '%s' not "
178830588217SMike Christensen 		    "associated with a port" DS_EOL, svc->cap.svc_id);
178930588217SMike Christensen 		return (-1);
179030588217SMike Christensen 	}
179130588217SMike Christensen 
179230588217SMike Christensen 	mutex_enter(&port->lock);
179330588217SMike Christensen 
179430588217SMike Christensen 	/* check on the LDC to Zeus */
179530588217SMike Christensen 	if (port->ldc.state != LDC_UP) {
179630588217SMike Christensen 		/* can not send message */
179730588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: unreg_req>: channel %ld is not up"
179830588217SMike Christensen 		    DS_EOL, PORTID(port), port->ldc.id);
179930588217SMike Christensen 		mutex_exit(&port->lock);
180030588217SMike Christensen 		return (-1);
180130588217SMike Christensen 	}
180230588217SMike Christensen 
180330588217SMike Christensen 	/* make sure port is ready */
180430588217SMike Christensen 	if (port->state != DS_PORT_READY) {
180530588217SMike Christensen 		/* can not send message */
180630588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: unreg_req>: port is not ready" DS_EOL,
180730588217SMike Christensen 		    PORTID(port));
180830588217SMike Christensen 		mutex_exit(&port->lock);
180930588217SMike Christensen 		return (-1);
181030588217SMike Christensen 	}
181130588217SMike Christensen 
181230588217SMike Christensen 	mutex_exit(&port->lock);
181330588217SMike Christensen 
181430588217SMike Christensen 	msglen = DS_HDR_SZ + sizeof (ds_unreg_req_t);
181530588217SMike Christensen 	msg = DS_MALLOC(msglen);
181630588217SMike Christensen 
181730588217SMike Christensen 	/* copy in the header data */
181830588217SMike Christensen 	hdr = (ds_hdr_t *)msg;
181930588217SMike Christensen 	hdr->msg_type = DS_UNREG;
182030588217SMike Christensen 	hdr->payload_len = sizeof (ds_unreg_req_t);
182130588217SMike Christensen 
182230588217SMike Christensen 	req = (ds_unreg_req_t *)(msg + DS_HDR_SZ);
182330588217SMike Christensen 	if (svc->flags & DSSF_ISCLIENT) {
182430588217SMike Christensen 		req->svc_handle = svc->svc_hdl;
182530588217SMike Christensen 	} else {
182630588217SMike Christensen 		req->svc_handle = svc->hdl;
182730588217SMike Christensen 	}
182830588217SMike Christensen 
182930588217SMike Christensen 	/* send the message */
183030588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: unreg_req>: '%s' hdl=0x%llx" DS_EOL,
183130588217SMike Christensen 	    PORTID(port), (svc->cap.svc_id) ? svc->cap.svc_id : "NULL",
183230588217SMike Christensen 	    (u_longlong_t)svc->hdl);
183330588217SMike Christensen 
183430588217SMike Christensen 	if ((rv = ds_send_msg(port, msg, msglen)) != 0) {
183530588217SMike Christensen 		rv = -1;
183630588217SMike Christensen 	}
183730588217SMike Christensen 	DS_FREE(msg, msglen);
183830588217SMike Christensen 
183930588217SMike Christensen 	return (rv);
184030588217SMike Christensen }
184130588217SMike Christensen 
184230588217SMike Christensen static void
ds_send_unreg_nack(ds_port_t * port,ds_svc_hdl_t bad_hdl)184330588217SMike Christensen ds_send_unreg_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl)
184430588217SMike Christensen {
184530588217SMike Christensen 	caddr_t		msg;
184630588217SMike Christensen 	size_t		msglen;
184730588217SMike Christensen 	ds_hdr_t	*hdr;
184830588217SMike Christensen 	ds_unreg_nack_t	*nack;
184930588217SMike Christensen 
185030588217SMike Christensen 	mutex_enter(&port->lock);
185130588217SMike Christensen 
185230588217SMike Christensen 	/* check on the LDC to Zeus */
185330588217SMike Christensen 	if (port->ldc.state != LDC_UP) {
185430588217SMike Christensen 		/* can not send message */
185530588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: unreg_nack>: channel %ld is not up"
185630588217SMike Christensen 		    DS_EOL, PORTID(port), port->ldc.id);
185730588217SMike Christensen 		mutex_exit(&port->lock);
185830588217SMike Christensen 		return;
185930588217SMike Christensen 	}
186030588217SMike Christensen 
186130588217SMike Christensen 	/* make sure port is ready */
186230588217SMike Christensen 	if (port->state != DS_PORT_READY) {
186330588217SMike Christensen 		/* can not send message */
186430588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: unreg_nack>: port is not ready"
186530588217SMike Christensen 		    DS_EOL, PORTID(port));
186630588217SMike Christensen 		mutex_exit(&port->lock);
186730588217SMike Christensen 		return;
186830588217SMike Christensen 	}
186930588217SMike Christensen 
187030588217SMike Christensen 	mutex_exit(&port->lock);
187130588217SMike Christensen 
187230588217SMike Christensen 	msglen = DS_HDR_SZ + sizeof (ds_unreg_nack_t);
187330588217SMike Christensen 	msg = DS_MALLOC(msglen);
187430588217SMike Christensen 
187530588217SMike Christensen 	/* copy in the header data */
187630588217SMike Christensen 	hdr = (ds_hdr_t *)msg;
187730588217SMike Christensen 	hdr->msg_type = DS_UNREG_NACK;
187830588217SMike Christensen 	hdr->payload_len = sizeof (ds_unreg_nack_t);
187930588217SMike Christensen 
188030588217SMike Christensen 	nack = (ds_unreg_nack_t *)(msg + DS_HDR_SZ);
188130588217SMike Christensen 	nack->svc_handle = bad_hdl;
188230588217SMike Christensen 
188330588217SMike Christensen 	/* send the message */
188430588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: unreg_nack>: hdl=0x%llx" DS_EOL,
188530588217SMike Christensen 	    PORTID(port), (u_longlong_t)bad_hdl);
188630588217SMike Christensen 
188730588217SMike Christensen 	(void) ds_send_msg(port, msg, msglen);
188830588217SMike Christensen 	DS_FREE(msg, msglen);
188930588217SMike Christensen }
189030588217SMike Christensen 
189130588217SMike Christensen static void
ds_send_data_nack(ds_port_t * port,ds_svc_hdl_t bad_hdl)189230588217SMike Christensen ds_send_data_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl)
189330588217SMike Christensen {
189430588217SMike Christensen 	caddr_t		msg;
189530588217SMike Christensen 	size_t		msglen;
189630588217SMike Christensen 	ds_hdr_t	*hdr;
189730588217SMike Christensen 	ds_data_nack_t	*nack;
189830588217SMike Christensen 
189930588217SMike Christensen 	mutex_enter(&port->lock);
190030588217SMike Christensen 
190130588217SMike Christensen 	/* check on the LDC to Zeus */
190230588217SMike Christensen 	if (port->ldc.state != LDC_UP) {
190330588217SMike Christensen 		/* can not send message */
190430588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: data_nack>: channel %ld is not up"
190530588217SMike Christensen 		    DS_EOL, PORTID(port), port->ldc.id);
190630588217SMike Christensen 		mutex_exit(&port->lock);
190730588217SMike Christensen 		return;
190830588217SMike Christensen 	}
190930588217SMike Christensen 
191030588217SMike Christensen 	/* make sure port is ready */
191130588217SMike Christensen 	if (port->state != DS_PORT_READY) {
191230588217SMike Christensen 		/* can not send message */
191330588217SMike Christensen 		cmn_err(CE_WARN, "ds@%lx: data_nack>: port is not ready" DS_EOL,
191430588217SMike Christensen 		    PORTID(port));
191530588217SMike Christensen 		mutex_exit(&port->lock);
191630588217SMike Christensen 		return;
191730588217SMike Christensen 	}
191830588217SMike Christensen 
191930588217SMike Christensen 	mutex_exit(&port->lock);
192030588217SMike Christensen 
192130588217SMike Christensen 	msglen = DS_HDR_SZ + sizeof (ds_data_nack_t);
192230588217SMike Christensen 	msg = DS_MALLOC(msglen);
192330588217SMike Christensen 
192430588217SMike Christensen 	/* copy in the header data */
192530588217SMike Christensen 	hdr = (ds_hdr_t *)msg;
192630588217SMike Christensen 	hdr->msg_type = DS_NACK;
192730588217SMike Christensen 	hdr->payload_len = sizeof (ds_data_nack_t);
192830588217SMike Christensen 
192930588217SMike Christensen 	nack = (ds_data_nack_t *)(msg + DS_HDR_SZ);
193030588217SMike Christensen 	nack->svc_handle = bad_hdl;
193130588217SMike Christensen 	nack->result = DS_INV_HDL;
193230588217SMike Christensen 
193330588217SMike Christensen 	/* send the message */
193430588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: data_nack>: hdl=0x%llx" DS_EOL,
193530588217SMike Christensen 	    PORTID(port), (u_longlong_t)bad_hdl);
193630588217SMike Christensen 
193730588217SMike Christensen 	(void) ds_send_msg(port, msg, msglen);
193830588217SMike Christensen 	DS_FREE(msg, msglen);
193930588217SMike Christensen }
194030588217SMike Christensen 
194130588217SMike Christensen /* END DS PROTOCOL SUPPORT FUNCTIONS */
194230588217SMike Christensen 
194330588217SMike Christensen #ifdef DEBUG
194430588217SMike Christensen 
194530588217SMike Christensen #define	BYTESPERLINE	8
194630588217SMike Christensen #define	LINEWIDTH	((BYTESPERLINE * 3) + (BYTESPERLINE + 2) + 1)
194730588217SMike Christensen #define	ASCIIOFFSET	((BYTESPERLINE * 3) + 2)
194830588217SMike Christensen #define	ISPRINT(c)	((c >= ' ') && (c <= '~'))
194930588217SMike Christensen 
195030588217SMike Christensen /*
195130588217SMike Christensen  * Output a buffer formatted with a set number of bytes on
195230588217SMike Christensen  * each line. Append each line with the ASCII equivalent of
195330588217SMike Christensen  * each byte if it falls within the printable ASCII range,
195430588217SMike Christensen  * and '.' otherwise.
195530588217SMike Christensen  */
195630588217SMike Christensen void
ds_dump_msg(void * vbuf,size_t len)195730588217SMike Christensen ds_dump_msg(void *vbuf, size_t len)
195830588217SMike Christensen {
195930588217SMike Christensen 	int	i, j;
196030588217SMike Christensen 	char	*curr;
196130588217SMike Christensen 	char	*aoff;
196230588217SMike Christensen 	char	line[LINEWIDTH];
196330588217SMike Christensen 	uint8_t	*buf = vbuf;
196430588217SMike Christensen 
196530588217SMike Christensen 	if (len > 128)
196630588217SMike Christensen 		len = 128;
196730588217SMike Christensen 
196830588217SMike Christensen 	/* walk the buffer one line at a time */
196930588217SMike Christensen 	for (i = 0; i < len; i += BYTESPERLINE) {
197030588217SMike Christensen 
197130588217SMike Christensen 		bzero(line, LINEWIDTH);
197230588217SMike Christensen 
197330588217SMike Christensen 		curr = line;
197430588217SMike Christensen 		aoff = line + ASCIIOFFSET;
197530588217SMike Christensen 
197630588217SMike Christensen 		/*
197730588217SMike Christensen 		 * Walk the bytes in the current line, storing
197830588217SMike Christensen 		 * the hex value for the byte as well as the
197930588217SMike Christensen 		 * ASCII representation in a temporary buffer.
198030588217SMike Christensen 		 * All ASCII values are placed at the end of
198130588217SMike Christensen 		 * the line.
198230588217SMike Christensen 		 */
198330588217SMike Christensen 		for (j = 0; (j < BYTESPERLINE) && ((i + j) < len); j++) {
198430588217SMike Christensen 			(void) sprintf(curr, " %02x", buf[i + j]);
198530588217SMike Christensen 			*aoff = (ISPRINT(buf[i + j])) ? buf[i + j] : '.';
198630588217SMike Christensen 			curr += 3;
198730588217SMike Christensen 			aoff++;
198830588217SMike Christensen 		}
198930588217SMike Christensen 
199030588217SMike Christensen 		/*
199130588217SMike Christensen 		 * Fill in to the start of the ASCII translation
199230588217SMike Christensen 		 * with spaces. This will only be necessary if
199330588217SMike Christensen 		 * this is the last line and there are not enough
199430588217SMike Christensen 		 * bytes to fill the whole line.
199530588217SMike Christensen 		 */
199630588217SMike Christensen 		while (curr != (line + ASCIIOFFSET))
199730588217SMike Christensen 			*curr++ = ' ';
199830588217SMike Christensen 
199930588217SMike Christensen 		cmn_err(CE_NOTE, "%s" DS_EOL, line);
200030588217SMike Christensen 	}
200130588217SMike Christensen }
200230588217SMike Christensen #endif /* DEBUG */
200330588217SMike Christensen 
200430588217SMike Christensen 
200530588217SMike Christensen /*
200630588217SMike Christensen  * Walk the table of registered services, executing the specified callback
200730588217SMike Christensen  * function for each service on a port. A non-zero return value from the
200830588217SMike Christensen  * callback is used to terminate the walk, not to indicate an error. Returns
200930588217SMike Christensen  * the index of the last service visited.
201030588217SMike Christensen  */
201130588217SMike Christensen int
ds_walk_svcs(svc_cb_t svc_cb,void * arg)201230588217SMike Christensen ds_walk_svcs(svc_cb_t svc_cb, void *arg)
201330588217SMike Christensen {
201430588217SMike Christensen 	int		idx;
201530588217SMike Christensen 	ds_svc_t	*svc;
201630588217SMike Christensen 
201730588217SMike Christensen 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
201830588217SMike Christensen 
201930588217SMike Christensen 	/* walk every table entry */
202030588217SMike Christensen 	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
202130588217SMike Christensen 		svc = ds_svcs.tbl[idx];
202230588217SMike Christensen 
202330588217SMike Christensen 		/* execute the callback */
202430588217SMike Christensen 		if ((*svc_cb)(svc, arg) != 0)
202530588217SMike Christensen 			break;
202630588217SMike Christensen 	}
202730588217SMike Christensen 
202830588217SMike Christensen 	return (idx);
202930588217SMike Christensen }
203030588217SMike Christensen 
203130588217SMike Christensen static int
ds_svc_isfree(ds_svc_t * svc,void * arg)203230588217SMike Christensen ds_svc_isfree(ds_svc_t *svc, void *arg)
203330588217SMike Christensen {
203430588217SMike Christensen 	_NOTE(ARGUNUSED(arg))
203530588217SMike Christensen 
203630588217SMike Christensen 	/*
203730588217SMike Christensen 	 * Looking for a free service. This may be a NULL entry
203830588217SMike Christensen 	 * in the table, or an unused structure that could be
203930588217SMike Christensen 	 * reused.
204030588217SMike Christensen 	 */
204130588217SMike Christensen 
204230588217SMike Christensen 	if (DS_SVC_ISFREE(svc)) {
204330588217SMike Christensen 		/* yes, it is free */
204430588217SMike Christensen 		return (1);
204530588217SMike Christensen 	}
204630588217SMike Christensen 
204730588217SMike Christensen 	/* not a candidate */
204830588217SMike Christensen 	return (0);
204930588217SMike Christensen }
205030588217SMike Christensen 
205130588217SMike Christensen int
ds_svc_ismatch(ds_svc_t * svc,void * arg)205230588217SMike Christensen ds_svc_ismatch(ds_svc_t *svc, void *arg)
205330588217SMike Christensen {
205430588217SMike Christensen 	if (DS_SVC_ISFREE(svc)) {
205530588217SMike Christensen 		return (0);
205630588217SMike Christensen 	}
205730588217SMike Christensen 
205830588217SMike Christensen 	if (strcmp(svc->cap.svc_id, arg) == 0 &&
205930588217SMike Christensen 	    (svc->flags & DSSF_ISCLIENT) == 0) {
206030588217SMike Christensen 		/* found a match */
206130588217SMike Christensen 		return (1);
206230588217SMike Christensen 	}
206330588217SMike Christensen 
206430588217SMike Christensen 	return (0);
206530588217SMike Christensen }
206630588217SMike Christensen 
206730588217SMike Christensen int
ds_svc_clnt_ismatch(ds_svc_t * svc,void * arg)206830588217SMike Christensen ds_svc_clnt_ismatch(ds_svc_t *svc, void *arg)
206930588217SMike Christensen {
207030588217SMike Christensen 	if (DS_SVC_ISFREE(svc)) {
207130588217SMike Christensen 		return (0);
207230588217SMike Christensen 	}
207330588217SMike Christensen 
207430588217SMike Christensen 	if (strcmp(svc->cap.svc_id, arg) == 0 &&
207530588217SMike Christensen 	    (svc->flags & DSSF_ISCLIENT) != 0) {
207630588217SMike Christensen 		/* found a match */
207730588217SMike Christensen 		return (1);
207830588217SMike Christensen 	}
207930588217SMike Christensen 
208030588217SMike Christensen 	return (0);
208130588217SMike Christensen }
208230588217SMike Christensen 
208330588217SMike Christensen int
ds_svc_free(ds_svc_t * svc,void * arg)208430588217SMike Christensen ds_svc_free(ds_svc_t *svc, void *arg)
208530588217SMike Christensen {
208630588217SMike Christensen 	_NOTE(ARGUNUSED(arg))
208730588217SMike Christensen 
208830588217SMike Christensen 	if (svc == NULL) {
208930588217SMike Christensen 		return (0);
209030588217SMike Christensen 	}
209130588217SMike Christensen 
209230588217SMike Christensen 	if (svc->cap.svc_id) {
209330588217SMike Christensen 		DS_FREE(svc->cap.svc_id, strlen(svc->cap.svc_id) + 1);
209430588217SMike Christensen 		svc->cap.svc_id = NULL;
209530588217SMike Christensen 	}
209630588217SMike Christensen 
209730588217SMike Christensen 	if (svc->cap.vers) {
209830588217SMike Christensen 		DS_FREE(svc->cap.vers, svc->cap.nvers * sizeof (ds_ver_t));
209930588217SMike Christensen 		svc->cap.vers = NULL;
210030588217SMike Christensen 	}
210130588217SMike Christensen 
210230588217SMike Christensen 	DS_FREE(svc, sizeof (ds_svc_t));
210330588217SMike Christensen 
210430588217SMike Christensen 	return (0);
210530588217SMike Christensen }
210630588217SMike Christensen 
2107ffc2bef0SMike Christensen static void
ds_set_svc_port_tried(char * svc_id,ds_port_t * port)2108ffc2bef0SMike Christensen ds_set_svc_port_tried(char *svc_id, ds_port_t *port)
2109ffc2bef0SMike Christensen {
2110ffc2bef0SMike Christensen 	int		idx;
2111ffc2bef0SMike Christensen 	ds_svc_t	*svc;
2112ffc2bef0SMike Christensen 
2113ffc2bef0SMike Christensen 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2114ffc2bef0SMike Christensen 
2115ffc2bef0SMike Christensen 	/* walk every table entry */
2116ffc2bef0SMike Christensen 	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
2117ffc2bef0SMike Christensen 		svc = ds_svcs.tbl[idx];
2118ffc2bef0SMike Christensen 		if (!DS_SVC_ISFREE(svc) && (svc->flags & DSSF_ISCLIENT) != 0 &&
2119ffc2bef0SMike Christensen 		    strcmp(svc_id, svc->cap.svc_id) == 0)
2120ffc2bef0SMike Christensen 			DS_PORTSET_ADD(svc->tried, PORTID(port));
2121ffc2bef0SMike Christensen 	}
2122ffc2bef0SMike Christensen }
2123ffc2bef0SMike Christensen 
212430588217SMike Christensen static int
ds_svc_register_onport(ds_svc_t * svc,ds_port_t * port)212530588217SMike Christensen ds_svc_register_onport(ds_svc_t *svc, ds_port_t *port)
212630588217SMike Christensen {
212730588217SMike Christensen 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
212830588217SMike Christensen 
212930588217SMike Christensen 	if (DS_SVC_ISFREE(svc))
213030588217SMike Christensen 		return (0);
213130588217SMike Christensen 
213230588217SMike Christensen 	if (!DS_PORT_IN_SET(svc->avail, PORTID(port)))
213330588217SMike Christensen 		return (0);
213430588217SMike Christensen 
2135ffc2bef0SMike Christensen 	if (DS_PORT_IN_SET(svc->tried, PORTID(port)))
2136ffc2bef0SMike Christensen 		return (0);
2137ffc2bef0SMike Christensen 
2138e9406929SMike Christensen 	if (!ds_port_is_ready(port))
2139e9406929SMike Christensen 		return (0);
2140e9406929SMike Christensen 
2141ffc2bef0SMike Christensen 	if ((svc->flags & DSSF_ISCLIENT) == 0) {
2142ffc2bef0SMike Christensen 		if (svc->state != DS_SVC_INACTIVE)
2143ffc2bef0SMike Christensen 			return (0);
2144e9406929SMike Christensen 		DS_PORTSET_ADD(svc->tried, PORTID(port));
2145ffc2bef0SMike Christensen 	} else {
2146ffc2bef0SMike Christensen 		ds_set_svc_port_tried(svc->cap.svc_id, port);
2147ffc2bef0SMike Christensen 
2148ffc2bef0SMike Christensen 		/*
2149ffc2bef0SMike Christensen 		 * Never send a client reg req to the SP.
2150ffc2bef0SMike Christensen 		 */
2151ffc2bef0SMike Christensen 		if (PORTID(port) == ds_sp_port_id) {
2152ffc2bef0SMike Christensen 			return (0);
2153ffc2bef0SMike Christensen 		}
2154ffc2bef0SMike Christensen 	}
215530588217SMike Christensen 
215630588217SMike Christensen 	if (ds_send_reg_req(svc, port) == 0) {
215730588217SMike Christensen 		/* register sent successfully */
215830588217SMike Christensen 		return (1);
215930588217SMike Christensen 	}
216030588217SMike Christensen 
216130588217SMike Christensen 	if ((svc->flags & DSSF_ISCLIENT) == 0) {
216230588217SMike Christensen 		/* reset the service */
216330588217SMike Christensen 		ds_reset_svc(svc, port);
216430588217SMike Christensen 	}
216530588217SMike Christensen 	return (0);
216630588217SMike Christensen }
216730588217SMike Christensen 
2168ffc2bef0SMike Christensen static int
ds_svc_register_onport_walker(ds_svc_t * svc,void * arg)2169ffc2bef0SMike Christensen ds_svc_register_onport_walker(ds_svc_t *svc, void *arg)
2170ffc2bef0SMike Christensen {
2171ffc2bef0SMike Christensen 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2172ffc2bef0SMike Christensen 
2173ffc2bef0SMike Christensen 	if (DS_SVC_ISFREE(svc))
2174ffc2bef0SMike Christensen 		return (0);
2175ffc2bef0SMike Christensen 
2176ffc2bef0SMike Christensen 	(void) ds_svc_register_onport(svc, arg);
2177ffc2bef0SMike Christensen 	return (0);
2178ffc2bef0SMike Christensen }
2179ffc2bef0SMike Christensen 
218030588217SMike Christensen int
ds_svc_register(ds_svc_t * svc,void * arg)218130588217SMike Christensen ds_svc_register(ds_svc_t *svc, void *arg)
218230588217SMike Christensen {
218330588217SMike Christensen 	_NOTE(ARGUNUSED(arg))
218430588217SMike Christensen 	ds_portset_t ports;
218530588217SMike Christensen 	ds_port_t *port;
218630588217SMike Christensen 	int	idx;
218730588217SMike Christensen 
218830588217SMike Christensen 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
218930588217SMike Christensen 
219030588217SMike Christensen 	if (DS_SVC_ISFREE(svc))
219130588217SMike Christensen 		return (0);
219230588217SMike Christensen 
21933ef557bfSMike Christensen 	DS_PORTSET_DUP(ports, svc->avail);
219430588217SMike Christensen 	if (svc->flags & DSSF_ISCLIENT) {
2195ffc2bef0SMike Christensen 		for (idx = 0; idx < DS_MAX_PORTS; idx++) {
2196ffc2bef0SMike Christensen 			if (DS_PORT_IN_SET(svc->tried, idx))
2197ffc2bef0SMike Christensen 				DS_PORTSET_DEL(ports, idx);
2198ffc2bef0SMike Christensen 		}
219930588217SMike Christensen 	} else if (svc->state != DS_SVC_INACTIVE)
220030588217SMike Christensen 		return (0);
220130588217SMike Christensen 
220230588217SMike Christensen 	if (DS_PORTSET_ISNULL(ports))
220330588217SMike Christensen 		return (0);
220430588217SMike Christensen 
220530588217SMike Christensen 	/*
220630588217SMike Christensen 	 * Attempt to register the service. Start with the lowest
220730588217SMike Christensen 	 * numbered port and continue until a registration message
220830588217SMike Christensen 	 * is sent successfully, or there are no ports left to try.
220930588217SMike Christensen 	 */
221030588217SMike Christensen 	for (idx = 0; idx < DS_MAX_PORTS; idx++) {
221130588217SMike Christensen 
221230588217SMike Christensen 		/*
221330588217SMike Christensen 		 * If the port is not in the available list,
221430588217SMike Christensen 		 * it is not a candidate for registration.
221530588217SMike Christensen 		 */
221630588217SMike Christensen 		if (!DS_PORT_IN_SET(ports, idx)) {
221730588217SMike Christensen 			continue;
221830588217SMike Christensen 		}
221930588217SMike Christensen 
222030588217SMike Christensen 		port = &ds_ports[idx];
222130588217SMike Christensen 		if (ds_svc_register_onport(svc, port)) {
222230588217SMike Christensen 			if ((svc->flags & DSSF_ISCLIENT) == 0)
222330588217SMike Christensen 				break;
222430588217SMike Christensen 		}
222530588217SMike Christensen 	}
222630588217SMike Christensen 
222730588217SMike Christensen 	return (0);
222830588217SMike Christensen }
222930588217SMike Christensen 
223030588217SMike Christensen static int
ds_svc_unregister(ds_svc_t * svc,void * arg)223130588217SMike Christensen ds_svc_unregister(ds_svc_t *svc, void *arg)
223230588217SMike Christensen {
223330588217SMike Christensen 	ds_port_t *port = (ds_port_t *)arg;
223430588217SMike Christensen 	ds_svc_hdl_t hdl;
223530588217SMike Christensen 
223630588217SMike Christensen 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
223730588217SMike Christensen 
223830588217SMike Christensen 	if (DS_SVC_ISFREE(svc)) {
223930588217SMike Christensen 		return (0);
224030588217SMike Christensen 	}
224130588217SMike Christensen 
224230588217SMike Christensen 	/* make sure the service is using this port */
224330588217SMike Christensen 	if (svc->port != port) {
224430588217SMike Christensen 		return (0);
224530588217SMike Christensen 	}
224630588217SMike Christensen 
224730588217SMike Christensen 	if (port) {
224830588217SMike Christensen 		DS_DBG(CE_NOTE, "ds@%lx: svc_unreg: id='%s', ver=%d.%d, "
224930588217SMike Christensen 		    " hdl=0x%09lx" DS_EOL, PORTID(port), svc->cap.svc_id,
225030588217SMike Christensen 		    svc->ver.major, svc->ver.minor, svc->hdl);
225130588217SMike Christensen 	} else {
225230588217SMike Christensen 		DS_DBG(CE_NOTE, "port=NULL: svc_unreg: id='%s', ver=%d.%d, "
225330588217SMike Christensen 		    " hdl=0x%09lx" DS_EOL, svc->cap.svc_id, svc->ver.major,
225430588217SMike Christensen 		    svc->ver.minor, svc->hdl);
225530588217SMike Christensen 	}
225630588217SMike Christensen 
225730588217SMike Christensen 	/* reset the service structure */
225830588217SMike Christensen 	ds_reset_svc(svc, port);
225930588217SMike Christensen 
226030588217SMike Christensen 	/* call the client unregister callback */
226130588217SMike Christensen 	if (svc->ops.ds_unreg_cb) {
226230588217SMike Christensen 		(*svc->ops.ds_unreg_cb)(svc->ops.cb_arg);
226330588217SMike Christensen 	}
226430588217SMike Christensen 
226530588217SMike Christensen 	/* increment the count in the handle to prevent reuse */
226630588217SMike Christensen 	hdl = DS_ALLOC_HDL(DS_HDL2IDX(svc->hdl), DS_HDL2COUNT(svc->hdl));
226730588217SMike Christensen 	if (DS_HDL_ISCLIENT(svc->hdl)) {
226830588217SMike Christensen 		DS_HDL_SET_ISCLIENT(hdl);
226930588217SMike Christensen 	}
227030588217SMike Christensen 	svc->hdl = hdl;
227130588217SMike Christensen 
227230588217SMike Christensen 	if (svc->state != DS_SVC_UNREG_PENDING) {
227330588217SMike Christensen 		/* try to initiate a new registration */
227430588217SMike Christensen 		(void) ds_svc_register(svc, NULL);
227530588217SMike Christensen 	}
227630588217SMike Christensen 
227730588217SMike Christensen 	return (0);
227830588217SMike Christensen }
227930588217SMike Christensen 
228030588217SMike Christensen static int
ds_svc_port_up(ds_svc_t * svc,void * arg)228130588217SMike Christensen ds_svc_port_up(ds_svc_t *svc, void *arg)
228230588217SMike Christensen {
228330588217SMike Christensen 	ds_port_t *port = (ds_port_t *)arg;
228430588217SMike Christensen 
228530588217SMike Christensen 	if (DS_SVC_ISFREE(svc)) {
228630588217SMike Christensen 		/* nothing to do */
228730588217SMike Christensen 		return (0);
228830588217SMike Christensen 	}
228930588217SMike Christensen 
229030588217SMike Christensen 	DS_PORTSET_ADD(svc->avail, port->id);
229130588217SMike Christensen 	DS_PORTSET_DEL(svc->tried, port->id);
229230588217SMike Christensen 
229330588217SMike Christensen 	return (0);
229430588217SMike Christensen }
229530588217SMike Christensen 
2296ffc2bef0SMike Christensen static void
ds_set_port_ready(ds_port_t * port,uint16_t major,uint16_t minor)2297ffc2bef0SMike Christensen ds_set_port_ready(ds_port_t *port, uint16_t major, uint16_t minor)
2298ffc2bef0SMike Christensen {
2299ffc2bef0SMike Christensen 	boolean_t was_ready;
2300ffc2bef0SMike Christensen 
2301ffc2bef0SMike Christensen 	mutex_enter(&port->lock);
2302ffc2bef0SMike Christensen 	was_ready = (port->state == DS_PORT_READY);
2303ffc2bef0SMike Christensen 	if (!was_ready) {
2304ffc2bef0SMike Christensen 		port->state = DS_PORT_READY;
2305ffc2bef0SMike Christensen 		port->ver.major = major;
2306ffc2bef0SMike Christensen 		port->ver.minor = minor;
2307ffc2bef0SMike Christensen 	}
2308ffc2bef0SMike Christensen 	mutex_exit(&port->lock);
2309ffc2bef0SMike Christensen 
2310ffc2bef0SMike Christensen 	if (!was_ready) {
2311ffc2bef0SMike Christensen 
2312ffc2bef0SMike Christensen 		/*
2313ffc2bef0SMike Christensen 		 * The port came up, so update all the services
2314ffc2bef0SMike Christensen 		 * with this information. Follow that up with an
2315ffc2bef0SMike Christensen 		 * attempt to register any service that is not
2316ffc2bef0SMike Christensen 		 * already registered.
2317ffc2bef0SMike Christensen 		 */
2318ffc2bef0SMike Christensen 		mutex_enter(&ds_svcs.lock);
2319ffc2bef0SMike Christensen 
2320ffc2bef0SMike Christensen 		(void) ds_walk_svcs(ds_svc_port_up, port);
2321ffc2bef0SMike Christensen 		(void) ds_walk_svcs(ds_svc_register_onport_walker, port);
2322ffc2bef0SMike Christensen 
2323ffc2bef0SMike Christensen 		mutex_exit(&ds_svcs.lock);
2324ffc2bef0SMike Christensen 	}
2325ffc2bef0SMike Christensen }
2326ffc2bef0SMike Christensen 
232730588217SMike Christensen ds_svc_t *
ds_alloc_svc(void)232830588217SMike Christensen ds_alloc_svc(void)
232930588217SMike Christensen {
233030588217SMike Christensen 	int		idx;
233130588217SMike Christensen 	uint_t		newmaxsvcs;
233230588217SMike Christensen 	ds_svc_t	**newtbl;
233330588217SMike Christensen 	ds_svc_t	*newsvc;
233430588217SMike Christensen 
233530588217SMike Christensen 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
233630588217SMike Christensen 
233730588217SMike Christensen 	idx = ds_walk_svcs(ds_svc_isfree, NULL);
233830588217SMike Christensen 
233930588217SMike Christensen 	if (idx != ds_svcs.maxsvcs) {
234030588217SMike Christensen 		goto found;
234130588217SMike Christensen 	}
234230588217SMike Christensen 
234330588217SMike Christensen 	/*
234430588217SMike Christensen 	 * There was no free space in the table. Grow
234530588217SMike Christensen 	 * the table to double its current size.
234630588217SMike Christensen 	 */
234730588217SMike Christensen 	newmaxsvcs = ds_svcs.maxsvcs * 2;
234830588217SMike Christensen 	newtbl = DS_MALLOC(newmaxsvcs * sizeof (ds_svc_t *));
234930588217SMike Christensen 
235030588217SMike Christensen 	/* copy old table data to the new table */
235130588217SMike Christensen 	(void) memcpy(newtbl, ds_svcs.tbl,
235230588217SMike Christensen 	    ds_svcs.maxsvcs * sizeof (ds_svc_t *));
235330588217SMike Christensen 
235430588217SMike Christensen 	/* clean up the old table */
235530588217SMike Christensen 	DS_FREE(ds_svcs.tbl, ds_svcs.maxsvcs * sizeof (ds_svc_t *));
235630588217SMike Christensen 	ds_svcs.tbl = newtbl;
235730588217SMike Christensen 	ds_svcs.maxsvcs = newmaxsvcs;
235830588217SMike Christensen 
235930588217SMike Christensen 	/* search for a free space again */
236030588217SMike Christensen 	idx = ds_walk_svcs(ds_svc_isfree, NULL);
236130588217SMike Christensen 
236230588217SMike Christensen 	/* the table is locked so should find a free slot */
236330588217SMike Christensen 	ASSERT(idx != ds_svcs.maxsvcs);
236430588217SMike Christensen 
236530588217SMike Christensen found:
236630588217SMike Christensen 	/* allocate a new svc structure if necessary */
236730588217SMike Christensen 	if ((newsvc = ds_svcs.tbl[idx]) == NULL) {
236830588217SMike Christensen 		/* allocate a new service */
236930588217SMike Christensen 		newsvc = DS_MALLOC(sizeof (ds_svc_t));
237030588217SMike Christensen 		ds_svcs.tbl[idx] = newsvc;
237130588217SMike Christensen 	}
237230588217SMike Christensen 
237330588217SMike Christensen 	/* fill in the handle */
237430588217SMike Christensen 	newsvc->hdl = DS_ALLOC_HDL(idx, DS_HDL2COUNT(newsvc->hdl));
237530588217SMike Christensen 	newsvc->state = DS_SVC_FREE;	/* Mark as free temporarily */
237630588217SMike Christensen 
237730588217SMike Christensen 	return (newsvc);
237830588217SMike Christensen }
237930588217SMike Christensen 
238030588217SMike Christensen static void
ds_reset_svc(ds_svc_t * svc,ds_port_t * port)238130588217SMike Christensen ds_reset_svc(ds_svc_t *svc, ds_port_t *port)
238230588217SMike Christensen {
238330588217SMike Christensen 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
238430588217SMike Christensen 
238530588217SMike Christensen 	if (svc->state != DS_SVC_UNREG_PENDING)
238630588217SMike Christensen 		svc->state = DS_SVC_INACTIVE;
238730588217SMike Christensen 	svc->ver_idx = 0;
238830588217SMike Christensen 	svc->ver.major = 0;
238930588217SMike Christensen 	svc->ver.minor = 0;
239030588217SMike Christensen 	svc->port = NULL;
239130588217SMike Christensen 	if (port) {
239230588217SMike Christensen 		DS_PORTSET_DEL(svc->avail, port->id);
239330588217SMike Christensen 	}
239430588217SMike Christensen }
239530588217SMike Christensen 
239630588217SMike Christensen ds_svc_t *
ds_get_svc(ds_svc_hdl_t hdl)239730588217SMike Christensen ds_get_svc(ds_svc_hdl_t hdl)
239830588217SMike Christensen {
239930588217SMike Christensen 	int		idx;
240030588217SMike Christensen 	ds_svc_t	*svc;
240130588217SMike Christensen 
240230588217SMike Christensen 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
240330588217SMike Christensen 
240430588217SMike Christensen 	if (hdl == DS_INVALID_HDL)
240530588217SMike Christensen 		return (NULL);
240630588217SMike Christensen 
240730588217SMike Christensen 	idx = DS_HDL2IDX(hdl);
240830588217SMike Christensen 
240930588217SMike Christensen 	/* check if index is out of bounds */
241030588217SMike Christensen 	if ((idx < 0) || (idx >= ds_svcs.maxsvcs))
241130588217SMike Christensen 		return (NULL);
241230588217SMike Christensen 
241330588217SMike Christensen 	svc = ds_svcs.tbl[idx];
241430588217SMike Christensen 
241530588217SMike Christensen 	/* check for a valid service */
241630588217SMike Christensen 	if (DS_SVC_ISFREE(svc))
241730588217SMike Christensen 		return (NULL);
241830588217SMike Christensen 
241930588217SMike Christensen 	/* make sure the handle is an exact match */
242030588217SMike Christensen 	if (svc->hdl != hdl)
242130588217SMike Christensen 		return (NULL);
242230588217SMike Christensen 
242330588217SMike Christensen 	return (svc);
242430588217SMike Christensen }
242530588217SMike Christensen 
242630588217SMike Christensen static void
ds_port_reset(ds_port_t * port)242730588217SMike Christensen ds_port_reset(ds_port_t *port)
242830588217SMike Christensen {
242930588217SMike Christensen 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
243030588217SMike Christensen 	ASSERT(MUTEX_HELD(&port->lock));
243130588217SMike Christensen 
243230588217SMike Christensen 	/* connection went down, mark everything inactive */
243330588217SMike Christensen 	(void) ds_walk_svcs(ds_svc_unregister, port);
243430588217SMike Christensen 
243530588217SMike Christensen 	port->ver_idx = 0;
243630588217SMike Christensen 	port->ver.major = 0;
243730588217SMike Christensen 	port->ver.minor = 0;
243830588217SMike Christensen 	port->state = DS_PORT_LDC_INIT;
243930588217SMike Christensen }
244030588217SMike Christensen 
244130588217SMike Christensen /*
244230588217SMike Christensen  * Verify that a version array is sorted as expected for the
244330588217SMike Christensen  * version negotiation to work correctly.
244430588217SMike Christensen  */
244530588217SMike Christensen ds_vers_check_t
ds_vers_isvalid(ds_ver_t * vers,int nvers)244630588217SMike Christensen ds_vers_isvalid(ds_ver_t *vers, int nvers)
244730588217SMike Christensen {
244830588217SMike Christensen 	uint16_t	curr_major;
244930588217SMike Christensen 	uint16_t	curr_minor;
245030588217SMike Christensen 	int		idx;
245130588217SMike Christensen 
245230588217SMike Christensen 	curr_major = vers[0].major;
245330588217SMike Christensen 	curr_minor = vers[0].minor;
245430588217SMike Christensen 
245530588217SMike Christensen 	/*
245630588217SMike Christensen 	 * Walk the version array, verifying correct ordering.
245730588217SMike Christensen 	 * The array must be sorted from highest supported
245830588217SMike Christensen 	 * version to lowest supported version.
245930588217SMike Christensen 	 */
246030588217SMike Christensen 	for (idx = 0; idx < nvers; idx++) {
246130588217SMike Christensen 		if (vers[idx].major > curr_major) {
246230588217SMike Christensen 			DS_DBG(CE_NOTE, "ds_vers_isvalid: version array has "
246330588217SMike Christensen 			    " increasing major versions" DS_EOL);
246430588217SMike Christensen 			return (DS_VERS_INCREASING_MAJOR_ERR);
246530588217SMike Christensen 		}
246630588217SMike Christensen 
246730588217SMike Christensen 		if (vers[idx].major < curr_major) {
246830588217SMike Christensen 			curr_major = vers[idx].major;
246930588217SMike Christensen 			curr_minor = vers[idx].minor;
247030588217SMike Christensen 			continue;
247130588217SMike Christensen 		}
247230588217SMike Christensen 
247330588217SMike Christensen 		if (vers[idx].minor > curr_minor) {
247430588217SMike Christensen 			DS_DBG(CE_NOTE, "ds_vers_isvalid: version array has "
247530588217SMike Christensen 			    " increasing minor versions" DS_EOL);
247630588217SMike Christensen 			return (DS_VERS_INCREASING_MINOR_ERR);
247730588217SMike Christensen 		}
247830588217SMike Christensen 
247930588217SMike Christensen 		curr_minor = vers[idx].minor;
248030588217SMike Christensen 	}
248130588217SMike Christensen 
248230588217SMike Christensen 	return (DS_VERS_OK);
248330588217SMike Christensen }
248430588217SMike Christensen 
248530588217SMike Christensen /*
248630588217SMike Christensen  * Extended user capability init.
248730588217SMike Christensen  */
248830588217SMike Christensen int
ds_ucap_init(ds_capability_t * cap,ds_clnt_ops_t * ops,uint32_t flags,int instance,ds_svc_hdl_t * hdlp)248930588217SMike Christensen ds_ucap_init(ds_capability_t *cap, ds_clnt_ops_t *ops, uint32_t flags,
249030588217SMike Christensen     int instance, ds_svc_hdl_t *hdlp)
249130588217SMike Christensen {
249230588217SMike Christensen 	ds_vers_check_t	status;
249330588217SMike Christensen 	ds_svc_t	*svc;
249430588217SMike Christensen 	int		rv = 0;
2495*cb453c7aSToomas Soome 	ds_svc_hdl_t	lb_hdl, hdl;
249630588217SMike Christensen 	int		is_loopback;
249730588217SMike Christensen 	int		is_client;
249830588217SMike Christensen 
249930588217SMike Christensen 	/* sanity check the args */
250030588217SMike Christensen 	if ((cap == NULL) || (ops == NULL)) {
250130588217SMike Christensen 		cmn_err(CE_NOTE, "%s: invalid arguments" DS_EOL, __func__);
250230588217SMike Christensen 		return (EINVAL);
250330588217SMike Christensen 	}
250430588217SMike Christensen 
250530588217SMike Christensen 	/* sanity check the capability specifier */
250630588217SMike Christensen 	if ((cap->svc_id == NULL) || (cap->vers == NULL) || (cap->nvers == 0)) {
250730588217SMike Christensen 		cmn_err(CE_NOTE, "%s: invalid capability specifier" DS_EOL,
250830588217SMike Christensen 		    __func__);
250930588217SMike Christensen 		return (EINVAL);
251030588217SMike Christensen 	}
251130588217SMike Christensen 
251230588217SMike Christensen 	/* sanity check the version array */
251330588217SMike Christensen 	if ((status = ds_vers_isvalid(cap->vers, cap->nvers)) != DS_VERS_OK) {
251430588217SMike Christensen 		cmn_err(CE_NOTE, "%s: invalid capability version array "
251530588217SMike Christensen 		    "for %s service: %s" DS_EOL, __func__, cap->svc_id,
251630588217SMike Christensen 		    (status == DS_VERS_INCREASING_MAJOR_ERR) ?
251730588217SMike Christensen 		    "increasing major versions" :
251830588217SMike Christensen 		    "increasing minor versions");
251930588217SMike Christensen 		return (EINVAL);
252030588217SMike Christensen 	}
252130588217SMike Christensen 
252230588217SMike Christensen 	/* data and register callbacks are required */
252330588217SMike Christensen 	if ((ops->ds_data_cb == NULL) || (ops->ds_reg_cb == NULL)) {
252430588217SMike Christensen 		cmn_err(CE_NOTE, "%s: invalid ops specifier for %s service"
252530588217SMike Christensen 		    DS_EOL, __func__, cap->svc_id);
252630588217SMike Christensen 		return (EINVAL);
252730588217SMike Christensen 	}
252830588217SMike Christensen 
252930588217SMike Christensen 	flags &= DSSF_USERFLAGS;
253030588217SMike Christensen 	is_client = flags & DSSF_ISCLIENT;
253130588217SMike Christensen 
253230588217SMike Christensen 	DS_DBG_USR(CE_NOTE, "%s: svc_id='%s', data_cb=0x%lx, cb_arg=0x%lx"
253330588217SMike Christensen 	    DS_EOL, __func__, cap->svc_id, PTR_TO_LONG(ops->ds_data_cb),
253430588217SMike Christensen 	    PTR_TO_LONG(ops->cb_arg));
253530588217SMike Christensen 
253630588217SMike Christensen 	mutex_enter(&ds_svcs.lock);
253730588217SMike Christensen 
253830588217SMike Christensen 	/* check if the service is already registered */
253930588217SMike Christensen 	if (i_ds_hdl_lookup(cap->svc_id, is_client, NULL, 1) == 1) {
254030588217SMike Christensen 		/* already registered */
2541e9406929SMike Christensen 		DS_DBG_USR(CE_NOTE, "Service '%s'/%s already registered" DS_EOL,
254230588217SMike Christensen 		    cap->svc_id,
254330588217SMike Christensen 		    (flags & DSSF_ISCLIENT) ? "client" : "service");
254430588217SMike Christensen 		mutex_exit(&ds_svcs.lock);
254530588217SMike Christensen 		return (EALREADY);
254630588217SMike Christensen 	}
254730588217SMike Christensen 
254830588217SMike Christensen 	svc = ds_alloc_svc();
254930588217SMike Christensen 	if (is_client) {
255030588217SMike Christensen 		DS_HDL_SET_ISCLIENT(svc->hdl);
255130588217SMike Christensen 	}
255230588217SMike Christensen 
255330588217SMike Christensen 	svc->state = DS_SVC_FREE;
255430588217SMike Christensen 	svc->svc_hdl = DS_BADHDL1;
255530588217SMike Christensen 
255630588217SMike Christensen 	svc->flags = flags;
255730588217SMike Christensen 	svc->drvi = instance;
255830588217SMike Christensen 	svc->drv_psp = NULL;
255930588217SMike Christensen 
256030588217SMike Christensen 	/*
2561f4b11d03SMike Christensen 	 * Check for loopback.  "pri" is a legacy service that assumes it
2562f4b11d03SMike Christensen 	 * will never use loopback mode.
256330588217SMike Christensen 	 */
2564f4b11d03SMike Christensen 	if (strcmp(cap->svc_id, "pri") == 0) {
2565f4b11d03SMike Christensen 		is_loopback = 0;
2566f4b11d03SMike Christensen 	} else if (i_ds_hdl_lookup(cap->svc_id, is_client == 0, &lb_hdl, 1)
2567f4b11d03SMike Christensen 	    == 1) {
2568a600f50dSMike Christensen 		if ((rv = ds_loopback_set_svc(svc, cap, &lb_hdl)) != 0) {
2569f4b11d03SMike Christensen 			DS_DBG_USR(CE_NOTE, "%s: ds_loopback_set_svc '%s' err "
2570f4b11d03SMike Christensen 			    " (%d)" DS_EOL, __func__, cap->svc_id, rv);
257130588217SMike Christensen 			mutex_exit(&ds_svcs.lock);
257230588217SMike Christensen 			return (rv);
257330588217SMike Christensen 		}
257430588217SMike Christensen 		is_loopback = 1;
257530588217SMike Christensen 	} else
257630588217SMike Christensen 		is_loopback = 0;
257730588217SMike Christensen 
257830588217SMike Christensen 	/* copy over all the client information */
257930588217SMike Christensen 	(void) memcpy(&svc->cap, cap, sizeof (ds_capability_t));
258030588217SMike Christensen 
258130588217SMike Christensen 	/* make a copy of the service name */
258230588217SMike Christensen 	svc->cap.svc_id = ds_strdup(cap->svc_id);
258330588217SMike Christensen 
258430588217SMike Christensen 	/* make a copy of the version array */
258530588217SMike Christensen 	svc->cap.vers = DS_MALLOC(cap->nvers * sizeof (ds_ver_t));
258630588217SMike Christensen 	(void) memcpy(svc->cap.vers, cap->vers, cap->nvers * sizeof (ds_ver_t));
258730588217SMike Christensen 
258830588217SMike Christensen 	/* copy the client ops vector */
258930588217SMike Christensen 	(void) memcpy(&svc->ops, ops, sizeof (ds_clnt_ops_t));
259030588217SMike Christensen 
259130588217SMike Christensen 	svc->state = DS_SVC_INACTIVE;
259230588217SMike Christensen 	svc->ver_idx = 0;
259330588217SMike Christensen 	DS_PORTSET_DUP(svc->avail, ds_allports);
259430588217SMike Christensen 	DS_PORTSET_SETNULL(svc->tried);
259530588217SMike Christensen 
259630588217SMike Christensen 	ds_svcs.nsvcs++;
259730588217SMike Christensen 
259830588217SMike Christensen 	hdl = svc->hdl;
259930588217SMike Christensen 
260030588217SMike Christensen 	/*
260130588217SMike Christensen 	 * kludge to allow user callback code to get handle and user args.
260230588217SMike Christensen 	 * Make sure the callback arg points to the svc structure.
260330588217SMike Christensen 	 */
260430588217SMike Christensen 	if ((flags & DSSF_ISUSER) != 0) {
260530588217SMike Christensen 		ds_cbarg_set_cookie(svc);
260630588217SMike Christensen 	}
260730588217SMike Christensen 
260830588217SMike Christensen 	if (is_loopback) {
260930588217SMike Christensen 		ds_loopback_register(hdl);
261030588217SMike Christensen 		ds_loopback_register(lb_hdl);
261130588217SMike Christensen 	}
261230588217SMike Christensen 
261330588217SMike Christensen 	/*
261430588217SMike Christensen 	 * If this is a client or a non-loopback service provider, send
261530588217SMike Christensen 	 * out register requests.
261630588217SMike Christensen 	 */
261730588217SMike Christensen 	if (!is_loopback || (flags & DSSF_ISCLIENT) != 0)
261830588217SMike Christensen 		(void) ds_svc_register(svc, NULL);
261930588217SMike Christensen 
262030588217SMike Christensen 	if (hdlp) {
262130588217SMike Christensen 		*hdlp = hdl;
262230588217SMike Christensen 	}
262330588217SMike Christensen 
262430588217SMike Christensen 	mutex_exit(&ds_svcs.lock);
262530588217SMike Christensen 
262630588217SMike Christensen 	DS_DBG_USR(CE_NOTE, "%s: service '%s' assigned handle 0x%09lx" DS_EOL,
262730588217SMike Christensen 	    __func__, svc->cap.svc_id, hdl);
262830588217SMike Christensen 
262930588217SMike Christensen 	return (0);
263030588217SMike Christensen }
263130588217SMike Christensen 
263230588217SMike Christensen /*
263330588217SMike Christensen  * ds_cap_init interface for previous revision.
263430588217SMike Christensen  */
263530588217SMike Christensen int
ds_cap_init(ds_capability_t * cap,ds_clnt_ops_t * ops)263630588217SMike Christensen ds_cap_init(ds_capability_t *cap, ds_clnt_ops_t *ops)
263730588217SMike Christensen {
263830588217SMike Christensen 	return (ds_ucap_init(cap, ops, 0, DS_INVALID_INSTANCE, NULL));
263930588217SMike Christensen }
264030588217SMike Christensen 
264130588217SMike Christensen /*
264230588217SMike Christensen  * Interface for ds_unreg_hdl in lds driver.
264330588217SMike Christensen  */
264430588217SMike Christensen int
ds_unreg_hdl(ds_svc_hdl_t hdl)264530588217SMike Christensen ds_unreg_hdl(ds_svc_hdl_t hdl)
264630588217SMike Christensen {
264730588217SMike Christensen 	ds_svc_t	*svc;
264830588217SMike Christensen 	int		is_loopback;
264930588217SMike Christensen 	ds_svc_hdl_t	lb_hdl;
265030588217SMike Christensen 
265130588217SMike Christensen 	DS_DBG_USR(CE_NOTE, "%s: hdl=0x%09lx" DS_EOL, __func__, hdl);
265230588217SMike Christensen 
265330588217SMike Christensen 	mutex_enter(&ds_svcs.lock);
265430588217SMike Christensen 	if ((svc = ds_get_svc(hdl)) == NULL) {
265530588217SMike Christensen 		mutex_exit(&ds_svcs.lock);
265630588217SMike Christensen 		DS_DBG_USR(CE_NOTE, "%s: unknown hdl: 0x%llx" DS_EOL, __func__,
265730588217SMike Christensen 		    (u_longlong_t)hdl);
265830588217SMike Christensen 		return (ENXIO);
265930588217SMike Christensen 	}
266030588217SMike Christensen 
266130588217SMike Christensen 	DS_DBG_USR(CE_NOTE, "%s: svcid='%s', hdl=0x%llx" DS_EOL, __func__,
266230588217SMike Christensen 	    svc->cap.svc_id, (u_longlong_t)svc->hdl);
266330588217SMike Christensen 
266430588217SMike Christensen 	svc->state = DS_SVC_UNREG_PENDING;
266530588217SMike Christensen 
266630588217SMike Christensen 	is_loopback = ((svc->flags & DSSF_LOOPBACK) != 0);
266730588217SMike Christensen 	lb_hdl = svc->svc_hdl;
266830588217SMike Christensen 
266930588217SMike Christensen 	if (svc->port) {
267030588217SMike Christensen 		(void) ds_send_unreg_req(svc);
267130588217SMike Christensen 	}
267230588217SMike Christensen 
267330588217SMike Christensen 	(void) ds_svc_unregister(svc, svc->port);
267430588217SMike Christensen 
267530588217SMike Christensen 	ds_delete_svc_entry(svc);
267630588217SMike Christensen 
267730588217SMike Christensen 	if (is_loopback) {
267830588217SMike Christensen 		ds_loopback_unregister(lb_hdl);
267930588217SMike Christensen 	}
268030588217SMike Christensen 
268130588217SMike Christensen 	mutex_exit(&ds_svcs.lock);
268230588217SMike Christensen 
268330588217SMike Christensen 	return (0);
268430588217SMike Christensen }
268530588217SMike Christensen 
268630588217SMike Christensen int
ds_cap_fini(ds_capability_t * cap)268730588217SMike Christensen ds_cap_fini(ds_capability_t *cap)
268830588217SMike Christensen {
268930588217SMike Christensen 	ds_svc_hdl_t	hdl;
269030588217SMike Christensen 	int rv;
269130588217SMike Christensen 	uint_t nhdls = 0;
269230588217SMike Christensen 
269330588217SMike Christensen 	DS_DBG(CE_NOTE, "%s: '%s'" DS_EOL, __func__, cap->svc_id);
269430588217SMike Christensen 	if ((rv = ds_hdl_lookup(cap->svc_id, 0, &hdl, 1, &nhdls)) != 0) {
269530588217SMike Christensen 		DS_DBG(CE_NOTE, "%s: ds_hdl_lookup '%s' err (%d)" DS_EOL,
269630588217SMike Christensen 		    __func__, cap->svc_id, rv);
269730588217SMike Christensen 		return (rv);
269830588217SMike Christensen 	}
269930588217SMike Christensen 
270030588217SMike Christensen 	if (nhdls == 0) {
270130588217SMike Christensen 		DS_DBG(CE_NOTE, "%s: no such service '%s'" DS_EOL,
270230588217SMike Christensen 		    __func__, cap->svc_id);
270330588217SMike Christensen 		return (ENXIO);
270430588217SMike Christensen 	}
270530588217SMike Christensen 
270630588217SMike Christensen 	if ((rv = ds_is_my_hdl(hdl, DS_INVALID_INSTANCE)) != 0) {
270730588217SMike Christensen 		DS_DBG(CE_NOTE, "%s: ds_is_my_handle err (%d)" DS_EOL, __func__,
270830588217SMike Christensen 		    rv);
270930588217SMike Christensen 		return (rv);
271030588217SMike Christensen 	}
271130588217SMike Christensen 
271230588217SMike Christensen 	if ((rv = ds_unreg_hdl(hdl)) != 0) {
271330588217SMike Christensen 		DS_DBG(CE_NOTE, "%s: ds_unreg_hdl err (%d)" DS_EOL, __func__,
271430588217SMike Christensen 		    rv);
271530588217SMike Christensen 		return (rv);
271630588217SMike Christensen 	}
271730588217SMike Christensen 
271830588217SMike Christensen 	return (0);
271930588217SMike Christensen }
272030588217SMike Christensen 
272130588217SMike Christensen int
ds_cap_send(ds_svc_hdl_t hdl,void * buf,size_t len)272230588217SMike Christensen ds_cap_send(ds_svc_hdl_t hdl, void *buf, size_t len)
272330588217SMike Christensen {
272430588217SMike Christensen 	int		rv;
272530588217SMike Christensen 	ds_hdr_t	*hdr;
272630588217SMike Christensen 	caddr_t		msg;
272730588217SMike Christensen 	size_t		msglen;
272830588217SMike Christensen 	size_t		hdrlen;
272930588217SMike Christensen 	caddr_t		payload;
273030588217SMike Christensen 	ds_svc_t	*svc;
273130588217SMike Christensen 	ds_port_t	*port;
273230588217SMike Christensen 	ds_data_handle_t *data;
273330588217SMike Christensen 	ds_svc_hdl_t	svc_hdl;
273430588217SMike Christensen 	int		is_client = 0;
273530588217SMike Christensen 
273630588217SMike Christensen 	DS_DBG(CE_NOTE, "%s: hdl: 0x%llx, buf: %lx, len: %ld" DS_EOL, __func__,
273730588217SMike Christensen 	    (u_longlong_t)hdl, (ulong_t)buf, len);
273830588217SMike Christensen 
273930588217SMike Christensen 	mutex_enter(&ds_svcs.lock);
274030588217SMike Christensen 
274130588217SMike Christensen 	if ((svc = ds_get_svc(hdl)) == NULL) {
274230588217SMike Christensen 		cmn_err(CE_WARN, "%s: invalid handle 0x%llx" DS_EOL, __func__,
274330588217SMike Christensen 		    (u_longlong_t)hdl);
274430588217SMike Christensen 		mutex_exit(&ds_svcs.lock);
274530588217SMike Christensen 		return (ENXIO);
274630588217SMike Christensen 	}
274730588217SMike Christensen 
274830588217SMike Christensen 	if (svc->state != DS_SVC_ACTIVE) {
274930588217SMike Christensen 		/* channel is up, but svc is not registered */
275030588217SMike Christensen 		DS_DBG(CE_NOTE, "%s: invalid service state 0x%x" DS_EOL,
275130588217SMike Christensen 		    __func__, svc->state);
275230588217SMike Christensen 		mutex_exit(&ds_svcs.lock);
275330588217SMike Christensen 		return (ENOTCONN);
275430588217SMike Christensen 	}
275530588217SMike Christensen 
275630588217SMike Christensen 	if (svc->flags & DSSF_LOOPBACK) {
275730588217SMike Christensen 		hdl = svc->svc_hdl;
275830588217SMike Christensen 		mutex_exit(&ds_svcs.lock);
275930588217SMike Christensen 		ds_loopback_send(hdl, buf, len);
276030588217SMike Christensen 		return (0);
276130588217SMike Christensen 	}
276230588217SMike Christensen 
276330588217SMike Christensen 	if ((port = svc->port) == NULL) {
276430588217SMike Christensen 		DS_DBG(CE_NOTE, "%s: service '%s' not associated with a port"
276530588217SMike Christensen 		    DS_EOL, __func__, svc->cap.svc_id);
276630588217SMike Christensen 		mutex_exit(&ds_svcs.lock);
276730588217SMike Christensen 		return (ECONNRESET);
276830588217SMike Christensen 	}
276930588217SMike Christensen 
277030588217SMike Christensen 	if (svc->flags & DSSF_ISCLIENT) {
277130588217SMike Christensen 		is_client = 1;
277230588217SMike Christensen 		svc_hdl = svc->svc_hdl;
277330588217SMike Christensen 	}
277430588217SMike Christensen 
277530588217SMike Christensen 	mutex_exit(&ds_svcs.lock);
277630588217SMike Christensen 
277730588217SMike Christensen 	/* check that the LDC channel is ready */
277830588217SMike Christensen 	if (port->ldc.state != LDC_UP) {
277930588217SMike Christensen 		DS_DBG(CE_NOTE, "%s: LDC channel is not up" DS_EOL, __func__);
278030588217SMike Christensen 		return (ECONNRESET);
278130588217SMike Christensen 	}
278230588217SMike Christensen 
278330588217SMike Christensen 	hdrlen = DS_HDR_SZ + sizeof (ds_data_handle_t);
278430588217SMike Christensen 
278530588217SMike Christensen 	msg = DS_MALLOC(len + hdrlen);
278630588217SMike Christensen 	hdr = (ds_hdr_t *)msg;
278730588217SMike Christensen 	payload = msg + hdrlen;
278830588217SMike Christensen 	msglen = len + hdrlen;
278930588217SMike Christensen 
279030588217SMike Christensen 	hdr->payload_len = len + sizeof (ds_data_handle_t);
279130588217SMike Christensen 	hdr->msg_type = DS_DATA;
279230588217SMike Christensen 
279330588217SMike Christensen 	data = (ds_data_handle_t *)(msg + DS_HDR_SZ);
279430588217SMike Christensen 	if (is_client) {
279530588217SMike Christensen 		data->svc_handle = svc_hdl;
279630588217SMike Christensen 	} else {
279730588217SMike Christensen 		data->svc_handle = hdl;
279830588217SMike Christensen 	}
279930588217SMike Christensen 
280030588217SMike Christensen 	if ((buf != NULL) && (len != 0)) {
280130588217SMike Christensen 		(void) memcpy(payload, buf, len);
280230588217SMike Christensen 	}
280330588217SMike Christensen 
280430588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: data>: hdl=0x%llx, len=%ld, "
280530588217SMike Christensen 	    " payload_len=%d" DS_EOL, PORTID(port), (u_longlong_t)svc->hdl,
280630588217SMike Christensen 	    msglen, hdr->payload_len);
280730588217SMike Christensen 	DS_DUMP_MSG(DS_DBG_FLAG_PRCL, msg, msglen);
280830588217SMike Christensen 
280930588217SMike Christensen 	if ((rv = ds_send_msg(port, msg, msglen)) != 0) {
281030588217SMike Christensen 		rv = (rv == EIO) ? ECONNRESET : rv;
281130588217SMike Christensen 	}
281230588217SMike Christensen 	DS_FREE(msg, msglen);
281330588217SMike Christensen 
281430588217SMike Christensen 	return (rv);
281530588217SMike Christensen }
281630588217SMike Christensen 
281730588217SMike Christensen void
ds_port_common_init(ds_port_t * port)281830588217SMike Christensen ds_port_common_init(ds_port_t *port)
281930588217SMike Christensen {
282030588217SMike Christensen 	int rv;
282130588217SMike Christensen 
282230588217SMike Christensen 	if ((port->flags & DS_PORT_MUTEX_INITED) == 0) {
282330588217SMike Christensen 		mutex_init(&port->lock, NULL, MUTEX_DRIVER, NULL);
282430588217SMike Christensen 		mutex_init(&port->tx_lock, NULL, MUTEX_DRIVER, NULL);
282530588217SMike Christensen 		mutex_init(&port->rcv_lock, NULL, MUTEX_DRIVER, NULL);
282630588217SMike Christensen 		port->flags |= DS_PORT_MUTEX_INITED;
282730588217SMike Christensen 	}
282830588217SMike Christensen 
282930588217SMike Christensen 	port->state = DS_PORT_INIT;
283030588217SMike Christensen 	DS_PORTSET_ADD(ds_allports, port->id);
283130588217SMike Christensen 
283230588217SMike Christensen 	ds_sys_port_init(port);
283330588217SMike Christensen 
283430588217SMike Christensen 	mutex_enter(&port->lock);
283530588217SMike Christensen 	rv = ds_ldc_init(port);
283630588217SMike Christensen 	mutex_exit(&port->lock);
283730588217SMike Christensen 
283830588217SMike Christensen 	/*
283930588217SMike Christensen 	 * If LDC successfully init'ed, try to kick off protocol for this port.
284030588217SMike Christensen 	 */
284130588217SMike Christensen 	if (rv == 0) {
284230588217SMike Christensen 		ds_handle_up_event(port);
284330588217SMike Christensen 	}
284430588217SMike Christensen }
284530588217SMike Christensen 
284630588217SMike Christensen void
ds_port_common_fini(ds_port_t * port)2847beba1dd8SMike Christensen ds_port_common_fini(ds_port_t *port)
284830588217SMike Christensen {
2849beba1dd8SMike Christensen 	ASSERT(MUTEX_HELD(&port->lock));
285030588217SMike Christensen 
2851beba1dd8SMike Christensen 	port->state = DS_PORT_FREE;
285230588217SMike Christensen 
285330588217SMike Christensen 	DS_PORTSET_DEL(ds_allports, port->id);
285430588217SMike Christensen 
285530588217SMike Christensen 	ds_sys_port_fini(port);
285630588217SMike Christensen }
285730588217SMike Christensen 
285830588217SMike Christensen /*
285930588217SMike Christensen  * Initialize table of registered service classes
286030588217SMike Christensen  */
286130588217SMike Christensen void
ds_init_svcs_tbl(uint_t nentries)286230588217SMike Christensen ds_init_svcs_tbl(uint_t nentries)
286330588217SMike Christensen {
286430588217SMike Christensen 	int	tblsz;
286530588217SMike Christensen 
286630588217SMike Christensen 	ds_svcs.maxsvcs = nentries;
286730588217SMike Christensen 
286830588217SMike Christensen 	tblsz = ds_svcs.maxsvcs * sizeof (ds_svc_t *);
286930588217SMike Christensen 	ds_svcs.tbl = (ds_svc_t **)DS_MALLOC(tblsz);
287030588217SMike Christensen 
287130588217SMike Christensen 	ds_svcs.nsvcs = 0;
287230588217SMike Christensen }
287330588217SMike Christensen 
287430588217SMike Christensen /*
287530588217SMike Christensen  * Find the max and min version supported.
287630588217SMike Christensen  * Hacked from zeus workspace, support.c
287730588217SMike Christensen  */
287830588217SMike Christensen static void
min_max_versions(int num_versions,ds_ver_t * sup_versionsp,uint16_t * min_major,uint16_t * max_major)287930588217SMike Christensen min_max_versions(int num_versions, ds_ver_t *sup_versionsp,
288030588217SMike Christensen     uint16_t *min_major, uint16_t *max_major)
288130588217SMike Christensen {
288230588217SMike Christensen 	int i;
288330588217SMike Christensen 
288430588217SMike Christensen 	*min_major = sup_versionsp[0].major;
288530588217SMike Christensen 	*max_major = *min_major;
288630588217SMike Christensen 
288730588217SMike Christensen 	for (i = 1; i < num_versions; i++) {
288830588217SMike Christensen 		if (sup_versionsp[i].major < *min_major)
288930588217SMike Christensen 			*min_major = sup_versionsp[i].major;
289030588217SMike Christensen 
289130588217SMike Christensen 		if (sup_versionsp[i].major > *max_major)
289230588217SMike Christensen 			*max_major = sup_versionsp[i].major;
289330588217SMike Christensen 	}
289430588217SMike Christensen }
289530588217SMike Christensen 
289630588217SMike Christensen /*
289730588217SMike Christensen  * Check whether the major and minor numbers requested by the peer can be
289830588217SMike Christensen  * satisfied. If the requested major is supported, true is returned, and the
289930588217SMike Christensen  * agreed minor is returned in new_minor. If the requested major is not
290030588217SMike Christensen  * supported, the routine returns false, and the closest major is returned in
290130588217SMike Christensen  * *new_major, upon which the peer should re-negotiate. The closest major is
290230588217SMike Christensen  * the just lower that the requested major number.
290330588217SMike Christensen  *
290430588217SMike Christensen  * Hacked from zeus workspace, support.c
290530588217SMike Christensen  */
290630588217SMike Christensen boolean_t
negotiate_version(int num_versions,ds_ver_t * sup_versionsp,uint16_t req_major,uint16_t * new_majorp,uint16_t * new_minorp)290730588217SMike Christensen negotiate_version(int num_versions, ds_ver_t *sup_versionsp,
290830588217SMike Christensen     uint16_t req_major, uint16_t *new_majorp, uint16_t *new_minorp)
290930588217SMike Christensen {
291030588217SMike Christensen 	int i;
291130588217SMike Christensen 	uint16_t major, lower_major;
291230588217SMike Christensen 	uint16_t min_major = 0, max_major;
291330588217SMike Christensen 	boolean_t found_match = B_FALSE;
291430588217SMike Christensen 
291530588217SMike Christensen 	min_max_versions(num_versions, sup_versionsp, &min_major, &max_major);
291630588217SMike Christensen 
291730588217SMike Christensen 	DS_DBG(CE_NOTE, "negotiate_version: req_major = %u, min = %u, max = %u"
291830588217SMike Christensen 	    DS_EOL, req_major, min_major, max_major);
291930588217SMike Christensen 
292030588217SMike Christensen 	/*
292130588217SMike Christensen 	 * If the minimum version supported is greater than
292230588217SMike Christensen 	 * the version requested, return the lowest version
292330588217SMike Christensen 	 * supported
292430588217SMike Christensen 	 */
292530588217SMike Christensen 	if (min_major > req_major) {
292630588217SMike Christensen 		*new_majorp = min_major;
292730588217SMike Christensen 		return (B_FALSE);
292830588217SMike Christensen 	}
292930588217SMike Christensen 
293030588217SMike Christensen 	/*
293130588217SMike Christensen 	 * If the largest version supported is lower than
293230588217SMike Christensen 	 * the version requested, return the largest version
293330588217SMike Christensen 	 * supported
293430588217SMike Christensen 	 */
293530588217SMike Christensen 	if (max_major < req_major) {
293630588217SMike Christensen 		*new_majorp = max_major;
293730588217SMike Christensen 		return (B_FALSE);
293830588217SMike Christensen 	}
293930588217SMike Christensen 
294030588217SMike Christensen 	/*
294130588217SMike Christensen 	 * Now we know that the requested version lies between the
294230588217SMike Christensen 	 * min and max versions supported. Check if the requested
294330588217SMike Christensen 	 * major can be found in supported versions.
294430588217SMike Christensen 	 */
294530588217SMike Christensen 	lower_major = min_major;
294630588217SMike Christensen 	for (i = 0; i < num_versions; i++) {
294730588217SMike Christensen 		major = sup_versionsp[i].major;
294830588217SMike Christensen 		if (major == req_major) {
294930588217SMike Christensen 			found_match = B_TRUE;
295030588217SMike Christensen 			*new_majorp = req_major;
295130588217SMike Christensen 			*new_minorp = sup_versionsp[i].minor;
295230588217SMike Christensen 			break;
295330588217SMike Christensen 		} else {
295430588217SMike Christensen 			if ((major < req_major) && (major > lower_major))
295530588217SMike Christensen 				lower_major = major;
295630588217SMike Christensen 		}
295730588217SMike Christensen 	}
295830588217SMike Christensen 
295930588217SMike Christensen 	/*
296030588217SMike Christensen 	 * If no match is found, return the closest available number
296130588217SMike Christensen 	 */
296230588217SMike Christensen 	if (!found_match)
296330588217SMike Christensen 		*new_majorp = lower_major;
296430588217SMike Christensen 
296530588217SMike Christensen 	return (found_match);
296630588217SMike Christensen }
296730588217SMike Christensen 
296830588217SMike Christensen /*
296930588217SMike Christensen  * Specific errno's that are used by ds.c and ldc.c
297030588217SMike Christensen  */
297130588217SMike Christensen static struct {
297230588217SMike Christensen 	int ds_errno;
297330588217SMike Christensen 	char *estr;
297430588217SMike Christensen } ds_errno_to_str_tab[] = {
297530588217SMike Christensen 	{ EIO,		"I/O error" },
297630588217SMike Christensen 	{ ENXIO,	"No such device or address" },
297730588217SMike Christensen 	{ EAGAIN,	"Resource temporarily unavailable" },
297830588217SMike Christensen 	{ ENOMEM,	"Not enough space" },
297930588217SMike Christensen 	{ EACCES,	"Permission denied" },
298030588217SMike Christensen 	{ EFAULT,	"Bad address" },
298130588217SMike Christensen 	{ EBUSY,	"Device busy" },
298230588217SMike Christensen 	{ EINVAL,	"Invalid argument" },
298330588217SMike Christensen 	{ ENOSPC,	"No space left on device" },
298430588217SMike Christensen 	{ ENOMSG,	"No message of desired type" },
298530588217SMike Christensen #ifdef	ECHRNG
298630588217SMike Christensen 	{ ECHRNG,	"Channel number out of range" },
298730588217SMike Christensen #endif
298830588217SMike Christensen 	{ ENOTSUP,	"Operation not supported" },
298930588217SMike Christensen 	{ EMSGSIZE,	"Message too long" },
299030588217SMike Christensen 	{ EADDRINUSE,	"Address already in use" },
299130588217SMike Christensen 	{ ECONNRESET,	"Connection reset by peer" },
299230588217SMike Christensen 	{ ENOBUFS,	"No buffer space available" },
299330588217SMike Christensen 	{ ENOTCONN,	"Socket is not connected" },
299430588217SMike Christensen 	{ ECONNREFUSED,	"Connection refused" },
299530588217SMike Christensen 	{ EALREADY,	"Operation already in progress" },
299630588217SMike Christensen 	{ 0,		NULL },
299730588217SMike Christensen };
299830588217SMike Christensen 
299930588217SMike Christensen char *
ds_errno_to_str(int ds_errno,char * ebuf)300030588217SMike Christensen ds_errno_to_str(int ds_errno, char *ebuf)
300130588217SMike Christensen {
300230588217SMike Christensen 	int i, en;
300330588217SMike Christensen 
300430588217SMike Christensen 	for (i = 0; (en = ds_errno_to_str_tab[i].ds_errno) != 0; i++) {
300530588217SMike Christensen 		if (en == ds_errno) {
300630588217SMike Christensen 			(void) strcpy(ebuf, ds_errno_to_str_tab[i].estr);
300730588217SMike Christensen 			return (ebuf);
300830588217SMike Christensen 		}
300930588217SMike Christensen 	}
301030588217SMike Christensen 
301130588217SMike Christensen 	(void) sprintf(ebuf, "ds_errno (%d)", ds_errno);
301230588217SMike Christensen 	return (ebuf);
301330588217SMike Christensen }
301430588217SMike Christensen 
301530588217SMike Christensen static void
ds_loopback_register(ds_svc_hdl_t hdl)301630588217SMike Christensen ds_loopback_register(ds_svc_hdl_t hdl)
301730588217SMike Christensen {
3018a600f50dSMike Christensen 	ds_ver_t ds_ver;
301930588217SMike Christensen 	ds_svc_t *svc;
302030588217SMike Christensen 
302130588217SMike Christensen 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
302230588217SMike Christensen 	DS_DBG_LOOP(CE_NOTE, "%s: entered hdl: 0x%llx" DS_EOL, __func__,
302330588217SMike Christensen 	    (u_longlong_t)hdl);
302430588217SMike Christensen 	if ((svc = ds_get_svc(hdl)) == NULL) {
302530588217SMike Christensen 		DS_DBG_LOOP(CE_NOTE, "%s: invalid hdl: 0x%llx" DS_EOL, __func__,
302630588217SMike Christensen 		    (u_longlong_t)hdl);
302730588217SMike Christensen 		return;
302830588217SMike Christensen 	}
3029a600f50dSMike Christensen 
303030588217SMike Christensen 	svc->state = DS_SVC_ACTIVE;
303130588217SMike Christensen 
303230588217SMike Christensen 	if (svc->ops.ds_reg_cb) {
303330588217SMike Christensen 		DS_DBG_LOOP(CE_NOTE, "%s: loopback regcb: hdl: 0x%llx" DS_EOL,
303430588217SMike Christensen 		    __func__, (u_longlong_t)hdl);
303530588217SMike Christensen 		ds_ver.major = svc->ver.major;
303630588217SMike Christensen 		ds_ver.minor = svc->ver.minor;
303730588217SMike Christensen 		(*svc->ops.ds_reg_cb)(svc->ops.cb_arg, &ds_ver, hdl);
303830588217SMike Christensen 	}
303930588217SMike Christensen }
304030588217SMike Christensen 
304130588217SMike Christensen static void
ds_loopback_unregister(ds_svc_hdl_t hdl)304230588217SMike Christensen ds_loopback_unregister(ds_svc_hdl_t hdl)
304330588217SMike Christensen {
304430588217SMike Christensen 	ds_svc_t *svc;
304530588217SMike Christensen 
304630588217SMike Christensen 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
304730588217SMike Christensen 	if ((svc = ds_get_svc(hdl)) == NULL) {
304830588217SMike Christensen 		DS_DBG_LOOP(CE_NOTE, "%s: invalid hdl: 0x%llx" DS_EOL, __func__,
304930588217SMike Christensen 		    (u_longlong_t)hdl);
305030588217SMike Christensen 		return;
305130588217SMike Christensen 	}
305230588217SMike Christensen 
305330588217SMike Christensen 	DS_DBG_LOOP(CE_NOTE, "%s: entered hdl: 0x%llx" DS_EOL, __func__,
305430588217SMike Christensen 	    (u_longlong_t)hdl);
305530588217SMike Christensen 
305630588217SMike Christensen 	svc->flags &= ~DSSF_LOOPBACK;
305730588217SMike Christensen 	svc->svc_hdl = DS_BADHDL2;
3058a600f50dSMike Christensen 	svc->state = DS_SVC_INACTIVE;
305930588217SMike Christensen 
306030588217SMike Christensen 	if (svc->ops.ds_unreg_cb) {
306130588217SMike Christensen 		DS_DBG_LOOP(CE_NOTE, "%s: loopback unregcb: hdl: 0x%llx" DS_EOL,
306230588217SMike Christensen 		    __func__, (u_longlong_t)hdl);
306330588217SMike Christensen 		(*svc->ops.ds_unreg_cb)(svc->ops.cb_arg);
306430588217SMike Christensen 	}
306530588217SMike Christensen }
306630588217SMike Christensen 
306730588217SMike Christensen static void
ds_loopback_send(ds_svc_hdl_t hdl,void * buf,size_t buflen)306830588217SMike Christensen ds_loopback_send(ds_svc_hdl_t hdl, void *buf, size_t buflen)
306930588217SMike Christensen {
307030588217SMike Christensen 	ds_svc_t *svc;
307130588217SMike Christensen 
307230588217SMike Christensen 	mutex_enter(&ds_svcs.lock);
307330588217SMike Christensen 	if ((svc = ds_get_svc(hdl)) == NULL) {
307430588217SMike Christensen 		mutex_exit(&ds_svcs.lock);
307530588217SMike Christensen 		DS_DBG_LOOP(CE_NOTE, "%s: invalid hdl: 0x%llx" DS_EOL, __func__,
307630588217SMike Christensen 		    (u_longlong_t)hdl);
307730588217SMike Christensen 		return;
307830588217SMike Christensen 	}
307930588217SMike Christensen 	mutex_exit(&ds_svcs.lock);
308030588217SMike Christensen 
308130588217SMike Christensen 	DS_DBG_LOOP(CE_NOTE, "%s: entered hdl: 0x%llx" DS_EOL, __func__,
308230588217SMike Christensen 	    (u_longlong_t)hdl);
308330588217SMike Christensen 
308430588217SMike Christensen 	if (svc->ops.ds_data_cb) {
308530588217SMike Christensen 		DS_DBG_LOOP(CE_NOTE, "%s: loopback datacb hdl: 0x%llx" DS_EOL,
308630588217SMike Christensen 		    __func__, (u_longlong_t)hdl);
308730588217SMike Christensen 		(*svc->ops.ds_data_cb)(svc->ops.cb_arg, buf, buflen);
308830588217SMike Christensen 	}
308930588217SMike Christensen }
309030588217SMike Christensen 
309130588217SMike Christensen static int
ds_loopback_set_svc(ds_svc_t * svc,ds_capability_t * cap,ds_svc_hdl_t * lb_hdlp)3092a600f50dSMike Christensen ds_loopback_set_svc(ds_svc_t *svc, ds_capability_t *cap, ds_svc_hdl_t *lb_hdlp)
309330588217SMike Christensen {
309430588217SMike Christensen 	ds_svc_t *lb_svc;
3095a600f50dSMike Christensen 	ds_svc_hdl_t lb_hdl = *lb_hdlp;
3096a600f50dSMike Christensen 	int i;
3097a600f50dSMike Christensen 	int match = 0;
3098a600f50dSMike Christensen 	uint16_t new_major;
3099a600f50dSMike Christensen 	uint16_t new_minor;
310030588217SMike Christensen 
310130588217SMike Christensen 	if ((lb_svc = ds_get_svc(lb_hdl)) == NULL) {
310230588217SMike Christensen 		DS_DBG_LOOP(CE_NOTE, "%s: loopback: hdl: 0x%llx invalid" DS_EOL,
310330588217SMike Christensen 		    __func__, (u_longlong_t)lb_hdl);
310430588217SMike Christensen 		return (ENXIO);
310530588217SMike Christensen 	}
3106a600f50dSMike Christensen 
3107a600f50dSMike Christensen 	/* negotiate a version between loopback services, if possible */
3108a600f50dSMike Christensen 	for (i = 0; i < lb_svc->cap.nvers && match == 0; i++) {
3109a600f50dSMike Christensen 		match = negotiate_version(cap->nvers, cap->vers,
3110a600f50dSMike Christensen 		    lb_svc->cap.vers[i].major, &new_major, &new_minor);
3111a600f50dSMike Christensen 	}
3112a600f50dSMike Christensen 	if (!match) {
3113a600f50dSMike Christensen 		DS_DBG_LOOP(CE_NOTE, "%s: loopback version negotiate failed"
3114a600f50dSMike Christensen 		    DS_EOL, __func__);
3115a600f50dSMike Christensen 		return (ENOTSUP);
3116a600f50dSMike Christensen 	}
3117bca70f54SMike Christensen 
3118bca70f54SMike Christensen 	/*
3119bca70f54SMike Christensen 	 * If a client service is not inactive, clone it.  If the service is
3120bca70f54SMike Christensen 	 * not a client service and has a reg req pending (usually from OBP
3121bca70f54SMike Christensen 	 * in boot state not acking/nacking reg req's), it's OK to ignore that,
3122bca70f54SMike Christensen 	 * since there are never multiple service clients.  Also reg req pending
3123bca70f54SMike Christensen 	 * only happens for non-client services, so it's OK to skip
3124bca70f54SMike Christensen 	 * this block that does client service cloning.
3125bca70f54SMike Christensen 	 */
3126bca70f54SMike Christensen 	if (lb_svc->state != DS_SVC_INACTIVE &&
3127bca70f54SMike Christensen 	    lb_svc->state != DS_SVC_REG_PENDING) {
3128a600f50dSMike Christensen 		DS_DBG_LOOP(CE_NOTE, "%s: loopback active: hdl: 0x%llx"
312930588217SMike Christensen 		    DS_EOL, __func__, (u_longlong_t)lb_hdl);
313030588217SMike Christensen 		if ((lb_svc->flags & DSSF_ISCLIENT) == 0) {
313130588217SMike Christensen 			DS_DBG_LOOP(CE_NOTE, "%s: loopback busy hdl: 0x%llx"
313230588217SMike Christensen 			    DS_EOL, __func__, (u_longlong_t)lb_hdl);
313330588217SMike Christensen 			return (EBUSY);
313430588217SMike Christensen 		}
3135a600f50dSMike Christensen 		svc->state = DS_SVC_INACTIVE;	/* prevent alloc'ing svc */
313630588217SMike Christensen 		lb_svc = ds_svc_clone(lb_svc);
313730588217SMike Christensen 		DS_DBG_LOOP(CE_NOTE, "%s: loopback clone: ohdl: 0x%llx "
313830588217SMike Christensen 		    "nhdl: 0x%llx" DS_EOL, __func__, (u_longlong_t)lb_hdl,
313930588217SMike Christensen 		    (u_longlong_t)lb_svc->hdl);
3140a600f50dSMike Christensen 		*lb_hdlp = lb_svc->hdl;
314130588217SMike Christensen 	}
314230588217SMike Christensen 
314330588217SMike Christensen 	svc->flags |= DSSF_LOOPBACK;
314430588217SMike Christensen 	svc->svc_hdl = lb_svc->hdl;
314530588217SMike Christensen 	svc->port = NULL;
3146a600f50dSMike Christensen 	svc->ver.major = new_major;
3147a600f50dSMike Christensen 	svc->ver.minor = new_minor;
314830588217SMike Christensen 
314930588217SMike Christensen 	lb_svc->flags |= DSSF_LOOPBACK;
315030588217SMike Christensen 	lb_svc->svc_hdl = svc->hdl;
315130588217SMike Christensen 	lb_svc->port = NULL;
3152a600f50dSMike Christensen 	lb_svc->ver.major = new_major;
3153a600f50dSMike Christensen 	lb_svc->ver.minor = new_minor;
315430588217SMike Christensen 
315530588217SMike Christensen 	DS_DBG_LOOP(CE_NOTE, "%s: setting loopback between: 0x%llx and 0x%llx"
315630588217SMike Christensen 	    DS_EOL, __func__, (u_longlong_t)svc->hdl,
315730588217SMike Christensen 	    (u_longlong_t)lb_svc->hdl);
315830588217SMike Christensen 	return (0);
315930588217SMike Christensen }
316030588217SMike Christensen 
316130588217SMike Christensen static ds_svc_t *
ds_find_clnt_svc_by_hdl_port(ds_svc_hdl_t hdl,ds_port_t * port)316230588217SMike Christensen ds_find_clnt_svc_by_hdl_port(ds_svc_hdl_t hdl, ds_port_t *port)
316330588217SMike Christensen {
316430588217SMike Christensen 	int		idx;
316530588217SMike Christensen 	ds_svc_t	*svc;
316630588217SMike Christensen 
316730588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: %s looking up clnt hdl: 0x%llx" DS_EOL,
316830588217SMike Christensen 	    PORTID(port), __func__, (u_longlong_t)hdl);
316930588217SMike Christensen 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
317030588217SMike Christensen 
317130588217SMike Christensen 	/* walk every table entry */
317230588217SMike Christensen 	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
317330588217SMike Christensen 		svc = ds_svcs.tbl[idx];
317430588217SMike Christensen 		if (DS_SVC_ISFREE(svc))
317530588217SMike Christensen 			continue;
317630588217SMike Christensen 		if ((svc->flags & DSSF_ISCLIENT) != 0 &&
317730588217SMike Christensen 		    svc->svc_hdl == hdl && svc->port == port) {
317830588217SMike Christensen 			DS_DBG_PRCL(CE_NOTE, "ds@%lx: %s found clnt hdl "
317930588217SMike Christensen 			    "0x%llx: svc%d" DS_EOL, PORTID(port), __func__,
318030588217SMike Christensen 			    (u_longlong_t)hdl, (uint_t)DS_HDL2IDX(svc->hdl));
318130588217SMike Christensen 			return (svc);
318230588217SMike Christensen 		}
318330588217SMike Christensen 	}
318430588217SMike Christensen 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: %s clnt hdl: 0x%llx not found" DS_EOL,
318530588217SMike Christensen 	    PORTID(port), __func__, (u_longlong_t)hdl);
318630588217SMike Christensen 
318730588217SMike Christensen 	return (NULL);
318830588217SMike Christensen }
318930588217SMike Christensen 
319030588217SMike Christensen static ds_svc_t *
ds_svc_clone(ds_svc_t * svc)319130588217SMike Christensen ds_svc_clone(ds_svc_t *svc)
319230588217SMike Christensen {
319330588217SMike Christensen 	ds_svc_t *newsvc;
319430588217SMike Christensen 	ds_svc_hdl_t hdl;
319530588217SMike Christensen 
319630588217SMike Christensen 	ASSERT(svc->flags & DSSF_ISCLIENT);
319730588217SMike Christensen 
319830588217SMike Christensen 	newsvc = ds_alloc_svc();
319930588217SMike Christensen 
320030588217SMike Christensen 	/* Can only clone clients for now */
320130588217SMike Christensen 	hdl = newsvc->hdl | DS_HDL_ISCLIENT_BIT;
320230588217SMike Christensen 	DS_DBG_USR(CE_NOTE, "%s: cloning client: old hdl: 0x%llx new hdl: "
320330588217SMike Christensen 	    "0x%llx" DS_EOL, __func__, (u_longlong_t)svc->hdl,
320430588217SMike Christensen 	    (u_longlong_t)hdl);
320530588217SMike Christensen 	(void) memcpy(newsvc, svc, sizeof (ds_svc_t));
320630588217SMike Christensen 	newsvc->hdl = hdl;
320730588217SMike Christensen 	newsvc->flags &= ~DSSF_LOOPBACK;
320830588217SMike Christensen 	newsvc->port = NULL;
320930588217SMike Christensen 	newsvc->svc_hdl = DS_BADHDL2;
321030588217SMike Christensen 	newsvc->cap.svc_id = ds_strdup(svc->cap.svc_id);
321130588217SMike Christensen 	newsvc->cap.vers = DS_MALLOC(svc->cap.nvers * sizeof (ds_ver_t));
321230588217SMike Christensen 	(void) memcpy(newsvc->cap.vers, svc->cap.vers,
321330588217SMike Christensen 	    svc->cap.nvers * sizeof (ds_ver_t));
321430588217SMike Christensen 
321530588217SMike Christensen 	/*
321630588217SMike Christensen 	 * Kludge to allow lds driver user callbacks to get access to current
321730588217SMike Christensen 	 * svc structure.  Arg could be index to svc table or some other piece
321830588217SMike Christensen 	 * of info to get to the svc table entry.
321930588217SMike Christensen 	 */
322030588217SMike Christensen 	if (newsvc->flags & DSSF_ISUSER) {
322130588217SMike Christensen 		newsvc->ops.cb_arg = (ds_cb_arg_t)(newsvc);
322230588217SMike Christensen 	}
322330588217SMike Christensen 	return (newsvc);
322430588217SMike Christensen }
322530588217SMike Christensen 
322630588217SMike Christensen /*
322730588217SMike Christensen  * Internal handle lookup function.
322830588217SMike Christensen  */
322930588217SMike Christensen static int
i_ds_hdl_lookup(char * service,uint_t is_client,ds_svc_hdl_t * hdlp,uint_t maxhdls)323030588217SMike Christensen i_ds_hdl_lookup(char *service, uint_t is_client, ds_svc_hdl_t *hdlp,
323130588217SMike Christensen     uint_t maxhdls)
323230588217SMike Christensen {
323330588217SMike Christensen 	int idx;
323430588217SMike Christensen 	int nhdls = 0;
323530588217SMike Christensen 	ds_svc_t *svc;
323630588217SMike Christensen 	uint32_t client_flag = is_client ? DSSF_ISCLIENT : 0;
323730588217SMike Christensen 
323830588217SMike Christensen 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
323930588217SMike Christensen 
324030588217SMike Christensen 	for (idx = 0; idx < ds_svcs.maxsvcs && nhdls < maxhdls; idx++) {
324130588217SMike Christensen 		svc = ds_svcs.tbl[idx];
324230588217SMike Christensen 		if (DS_SVC_ISFREE(svc))
324330588217SMike Christensen 			continue;
324430588217SMike Christensen 		if (strcmp(svc->cap.svc_id, service) == 0 &&
324530588217SMike Christensen 		    (svc->flags & DSSF_ISCLIENT) == client_flag) {
324630588217SMike Christensen 			if (hdlp != NULL && nhdls < maxhdls) {
324730588217SMike Christensen 				hdlp[nhdls] = svc->hdl;
324830588217SMike Christensen 				nhdls++;
324930588217SMike Christensen 			} else {
325030588217SMike Christensen 				nhdls++;
325130588217SMike Christensen 			}
325230588217SMike Christensen 		}
325330588217SMike Christensen 	}
325430588217SMike Christensen 	return (nhdls);
325530588217SMike Christensen }
325630588217SMike Christensen 
325730588217SMike Christensen /*
325830588217SMike Christensen  * Interface for ds_hdl_lookup in lds driver.
325930588217SMike Christensen  */
326030588217SMike Christensen int
ds_hdl_lookup(char * service,uint_t is_client,ds_svc_hdl_t * hdlp,uint_t maxhdls,uint_t * nhdlsp)326130588217SMike Christensen ds_hdl_lookup(char *service, uint_t is_client, ds_svc_hdl_t *hdlp,
326230588217SMike Christensen     uint_t maxhdls, uint_t *nhdlsp)
326330588217SMike Christensen {
326430588217SMike Christensen 	mutex_enter(&ds_svcs.lock);
326530588217SMike Christensen 	*nhdlsp = i_ds_hdl_lookup(service, is_client, hdlp, maxhdls);
326630588217SMike Christensen 	mutex_exit(&ds_svcs.lock);
326730588217SMike Christensen 	return (0);
326830588217SMike Christensen }
326930588217SMike Christensen 
327030588217SMike Christensen /*
327130588217SMike Christensen  * After an UNREG REQ, check if this is a client service with multiple
327230588217SMike Christensen  * handles.  If it is, then we can eliminate this entry.
327330588217SMike Christensen  */
327430588217SMike Christensen static void
ds_check_for_dup_services(ds_svc_t * svc)327530588217SMike Christensen ds_check_for_dup_services(ds_svc_t *svc)
327630588217SMike Christensen {
327730588217SMike Christensen 	if ((svc->flags & DSSF_ISCLIENT) != 0 &&
327830588217SMike Christensen 	    svc->state == DS_SVC_INACTIVE &&
327930588217SMike Christensen 	    i_ds_hdl_lookup(svc->cap.svc_id, 1, NULL, 2) == 2) {
328030588217SMike Christensen 		ds_delete_svc_entry(svc);
328130588217SMike Christensen 	}
328230588217SMike Christensen }
328330588217SMike Christensen 
328430588217SMike Christensen static void
ds_delete_svc_entry(ds_svc_t * svc)328530588217SMike Christensen ds_delete_svc_entry(ds_svc_t *svc)
328630588217SMike Christensen {
328730588217SMike Christensen 	ds_svc_hdl_t tmp_hdl;
328830588217SMike Christensen 
328930588217SMike Christensen 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
329030588217SMike Christensen 
329130588217SMike Christensen 	/*
329230588217SMike Christensen 	 * Clear out the structure, but do not deallocate the
329330588217SMike Christensen 	 * memory. It can be reused for the next registration.
329430588217SMike Christensen 	 */
329530588217SMike Christensen 	DS_FREE(svc->cap.svc_id, strlen(svc->cap.svc_id) + 1);
329630588217SMike Christensen 	DS_FREE(svc->cap.vers, svc->cap.nvers * sizeof (ds_ver_t));
329730588217SMike Christensen 
329830588217SMike Christensen 	/* save the handle to prevent reuse */
329930588217SMike Christensen 	tmp_hdl = svc->hdl;
330030588217SMike Christensen 	bzero((void *)svc, sizeof (ds_svc_t));
330130588217SMike Christensen 
330230588217SMike Christensen 	/* initialize for next use */
330330588217SMike Christensen 	svc->hdl = tmp_hdl;
330430588217SMike Christensen 	svc->state = DS_SVC_FREE;
330530588217SMike Christensen 
330630588217SMike Christensen 	ds_svcs.nsvcs--;
330730588217SMike Christensen }
3308