xref: /onnv-gate/usr/src/uts/sun4v/io/ds_common.c (revision 7697:ef43d2dd0b19)
1*7697SMichael.Christensen@Sun.COM /*
2*7697SMichael.Christensen@Sun.COM  * CDDL HEADER START
3*7697SMichael.Christensen@Sun.COM  *
4*7697SMichael.Christensen@Sun.COM  * The contents of this file are subject to the terms of the
5*7697SMichael.Christensen@Sun.COM  * Common Development and Distribution License (the "License").
6*7697SMichael.Christensen@Sun.COM  * You may not use this file except in compliance with the License.
7*7697SMichael.Christensen@Sun.COM  *
8*7697SMichael.Christensen@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*7697SMichael.Christensen@Sun.COM  * or http://www.opensolaris.org/os/licensing.
10*7697SMichael.Christensen@Sun.COM  * See the License for the specific language governing permissions
11*7697SMichael.Christensen@Sun.COM  * and limitations under the License.
12*7697SMichael.Christensen@Sun.COM  *
13*7697SMichael.Christensen@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
14*7697SMichael.Christensen@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*7697SMichael.Christensen@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
16*7697SMichael.Christensen@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
17*7697SMichael.Christensen@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
18*7697SMichael.Christensen@Sun.COM  *
19*7697SMichael.Christensen@Sun.COM  * CDDL HEADER END
20*7697SMichael.Christensen@Sun.COM  */
21*7697SMichael.Christensen@Sun.COM 
22*7697SMichael.Christensen@Sun.COM /*
23*7697SMichael.Christensen@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24*7697SMichael.Christensen@Sun.COM  * Use is subject to license terms.
25*7697SMichael.Christensen@Sun.COM  */
26*7697SMichael.Christensen@Sun.COM 
27*7697SMichael.Christensen@Sun.COM /*
28*7697SMichael.Christensen@Sun.COM  * Domain Services Module Common Code.
29*7697SMichael.Christensen@Sun.COM  *
30*7697SMichael.Christensen@Sun.COM  * This module is intended to be used by both Solaris and the VBSC
31*7697SMichael.Christensen@Sun.COM  * module.
32*7697SMichael.Christensen@Sun.COM  */
33*7697SMichael.Christensen@Sun.COM 
34*7697SMichael.Christensen@Sun.COM #include <sys/modctl.h>
35*7697SMichael.Christensen@Sun.COM #include <sys/ksynch.h>
36*7697SMichael.Christensen@Sun.COM #include <sys/taskq.h>
37*7697SMichael.Christensen@Sun.COM #include <sys/disp.h>
38*7697SMichael.Christensen@Sun.COM #include <sys/cmn_err.h>
39*7697SMichael.Christensen@Sun.COM #include <sys/note.h>
40*7697SMichael.Christensen@Sun.COM #include <sys/mach_descrip.h>
41*7697SMichael.Christensen@Sun.COM #include <sys/mdesc.h>
42*7697SMichael.Christensen@Sun.COM #include <sys/ldc.h>
43*7697SMichael.Christensen@Sun.COM #include <sys/ds.h>
44*7697SMichael.Christensen@Sun.COM #include <sys/ds_impl.h>
45*7697SMichael.Christensen@Sun.COM 
46*7697SMichael.Christensen@Sun.COM #ifndef MIN
47*7697SMichael.Christensen@Sun.COM #define	MIN(a, b)	((a) < (b) ? (a) : (b))
48*7697SMichael.Christensen@Sun.COM #endif
49*7697SMichael.Christensen@Sun.COM 
50*7697SMichael.Christensen@Sun.COM #define	DS_DECODE_BUF_LEN		30
51*7697SMichael.Christensen@Sun.COM 
52*7697SMichael.Christensen@Sun.COM /*
53*7697SMichael.Christensen@Sun.COM  * All DS ports in the system
54*7697SMichael.Christensen@Sun.COM  *
55*7697SMichael.Christensen@Sun.COM  * The list of DS ports is read in from the MD when the DS module is
56*7697SMichael.Christensen@Sun.COM  * initialized and is never modified. This eliminates the need for
57*7697SMichael.Christensen@Sun.COM  * locking to access the port array itself. Access to the individual
58*7697SMichael.Christensen@Sun.COM  * ports are synchronized at the port level.
59*7697SMichael.Christensen@Sun.COM  */
60*7697SMichael.Christensen@Sun.COM ds_port_t	ds_ports[DS_MAX_PORTS];
61*7697SMichael.Christensen@Sun.COM ds_portset_t	ds_allports;	/* all DS ports in the system */
62*7697SMichael.Christensen@Sun.COM 
63*7697SMichael.Christensen@Sun.COM /*
64*7697SMichael.Christensen@Sun.COM  * Table of registered services
65*7697SMichael.Christensen@Sun.COM  *
66*7697SMichael.Christensen@Sun.COM  * Locking: Accesses to the table of services are synchronized using
67*7697SMichael.Christensen@Sun.COM  *   a mutex lock. The reader lock must be held when looking up service
68*7697SMichael.Christensen@Sun.COM  *   information in the table. The writer lock must be held when any
69*7697SMichael.Christensen@Sun.COM  *   service information is being modified.
70*7697SMichael.Christensen@Sun.COM  */
71*7697SMichael.Christensen@Sun.COM ds_svcs_t	ds_svcs;
72*7697SMichael.Christensen@Sun.COM 
73*7697SMichael.Christensen@Sun.COM /*
74*7697SMichael.Christensen@Sun.COM  * Flag to prevent callbacks while in the middle of DS teardown.
75*7697SMichael.Christensen@Sun.COM  */
76*7697SMichael.Christensen@Sun.COM boolean_t ds_enabled = B_FALSE;	/* enable/disable taskq processing */
77*7697SMichael.Christensen@Sun.COM 
78*7697SMichael.Christensen@Sun.COM /*
79*7697SMichael.Christensen@Sun.COM  * Retry count and delay for LDC reads and writes
80*7697SMichael.Christensen@Sun.COM  */
81*7697SMichael.Christensen@Sun.COM #ifndef DS_DEFAULT_RETRIES
82*7697SMichael.Christensen@Sun.COM #define	DS_DEFAULT_RETRIES	10000	/* number of times to retry */
83*7697SMichael.Christensen@Sun.COM #endif
84*7697SMichael.Christensen@Sun.COM #ifndef DS_DEFAULT_DELAY
85*7697SMichael.Christensen@Sun.COM #define	DS_DEFAULT_DELAY	1000	/* usecs to wait between retries */
86*7697SMichael.Christensen@Sun.COM #endif
87*7697SMichael.Christensen@Sun.COM 
88*7697SMichael.Christensen@Sun.COM static int ds_retries = DS_DEFAULT_RETRIES;
89*7697SMichael.Christensen@Sun.COM static clock_t ds_delay = DS_DEFAULT_DELAY;
90*7697SMichael.Christensen@Sun.COM 
91*7697SMichael.Christensen@Sun.COM /*
92*7697SMichael.Christensen@Sun.COM  * Supported versions of the DS message protocol
93*7697SMichael.Christensen@Sun.COM  *
94*7697SMichael.Christensen@Sun.COM  * The version array must be sorted in order from the highest
95*7697SMichael.Christensen@Sun.COM  * supported version to the lowest. Support for a particular
96*7697SMichael.Christensen@Sun.COM  * <major>.<minor> version implies all lower minor versions of
97*7697SMichael.Christensen@Sun.COM  * that same major version are supported as well.
98*7697SMichael.Christensen@Sun.COM  */
99*7697SMichael.Christensen@Sun.COM static ds_ver_t ds_vers[] = { { 1, 0 } };
100*7697SMichael.Christensen@Sun.COM 
101*7697SMichael.Christensen@Sun.COM #define	DS_NUM_VER	(sizeof (ds_vers) / sizeof (ds_vers[0]))
102*7697SMichael.Christensen@Sun.COM 
103*7697SMichael.Christensen@Sun.COM 
104*7697SMichael.Christensen@Sun.COM /* incoming message handling functions */
105*7697SMichael.Christensen@Sun.COM typedef void (*ds_msg_handler_t)(ds_port_t *port, caddr_t buf, size_t len);
106*7697SMichael.Christensen@Sun.COM static void ds_handle_init_req(ds_port_t *port, caddr_t buf, size_t len);
107*7697SMichael.Christensen@Sun.COM static void ds_handle_init_ack(ds_port_t *port, caddr_t buf, size_t len);
108*7697SMichael.Christensen@Sun.COM static void ds_handle_init_nack(ds_port_t *port, caddr_t buf, size_t len);
109*7697SMichael.Christensen@Sun.COM static void ds_handle_reg_req(ds_port_t *port, caddr_t buf, size_t len);
110*7697SMichael.Christensen@Sun.COM static void ds_handle_reg_ack(ds_port_t *port, caddr_t buf, size_t len);
111*7697SMichael.Christensen@Sun.COM static void ds_handle_reg_nack(ds_port_t *port, caddr_t buf, size_t len);
112*7697SMichael.Christensen@Sun.COM static void ds_handle_unreg_req(ds_port_t *port, caddr_t buf, size_t len);
113*7697SMichael.Christensen@Sun.COM static void ds_handle_unreg_ack(ds_port_t *port, caddr_t buf, size_t len);
114*7697SMichael.Christensen@Sun.COM static void ds_handle_unreg_nack(ds_port_t *port, caddr_t buf, size_t len);
115*7697SMichael.Christensen@Sun.COM static void ds_handle_data(ds_port_t *port, caddr_t buf, size_t len);
116*7697SMichael.Christensen@Sun.COM static void ds_handle_nack(ds_port_t *port, caddr_t buf, size_t len);
117*7697SMichael.Christensen@Sun.COM 
118*7697SMichael.Christensen@Sun.COM /*
119*7697SMichael.Christensen@Sun.COM  * DS Message Handler Dispatch Table
120*7697SMichael.Christensen@Sun.COM  *
121*7697SMichael.Christensen@Sun.COM  * A table used to dispatch all incoming messages. This table
122*7697SMichael.Christensen@Sun.COM  * contains handlers for all the fixed message types, as well as
123*7697SMichael.Christensen@Sun.COM  * the the messages defined in the 1.0 version of the DS protocol.
124*7697SMichael.Christensen@Sun.COM  * The handlers are indexed based on the DS header msg_type values
125*7697SMichael.Christensen@Sun.COM  */
126*7697SMichael.Christensen@Sun.COM static const ds_msg_handler_t ds_msg_handlers[] = {
127*7697SMichael.Christensen@Sun.COM 	ds_handle_init_req,		/* DS_INIT_REQ */
128*7697SMichael.Christensen@Sun.COM 	ds_handle_init_ack,		/* DS_INIT_ACK */
129*7697SMichael.Christensen@Sun.COM 	ds_handle_init_nack,		/* DS_INIT_NACK */
130*7697SMichael.Christensen@Sun.COM 	ds_handle_reg_req,		/* DS_REG_REQ */
131*7697SMichael.Christensen@Sun.COM 	ds_handle_reg_ack,		/* DS_REG_ACK */
132*7697SMichael.Christensen@Sun.COM 	ds_handle_reg_nack,		/* DS_REG_NACK */
133*7697SMichael.Christensen@Sun.COM 	ds_handle_unreg_req,		/* DS_UNREG */
134*7697SMichael.Christensen@Sun.COM 	ds_handle_unreg_ack,		/* DS_UNREG_ACK */
135*7697SMichael.Christensen@Sun.COM 	ds_handle_unreg_nack,		/* DS_UNREG_NACK */
136*7697SMichael.Christensen@Sun.COM 	ds_handle_data,			/* DS_DATA */
137*7697SMichael.Christensen@Sun.COM 	ds_handle_nack			/* DS_NACK */
138*7697SMichael.Christensen@Sun.COM };
139*7697SMichael.Christensen@Sun.COM 
140*7697SMichael.Christensen@Sun.COM 
141*7697SMichael.Christensen@Sun.COM 
142*7697SMichael.Christensen@Sun.COM /* initialization functions */
143*7697SMichael.Christensen@Sun.COM static int ds_ldc_init(ds_port_t *port);
144*7697SMichael.Christensen@Sun.COM 
145*7697SMichael.Christensen@Sun.COM /* event processing functions */
146*7697SMichael.Christensen@Sun.COM static uint_t ds_ldc_cb(uint64_t event, caddr_t arg);
147*7697SMichael.Christensen@Sun.COM static int ds_recv_msg(ds_port_t *port, caddr_t msgp, size_t *sizep);
148*7697SMichael.Christensen@Sun.COM static void ds_handle_up_event(ds_port_t *port);
149*7697SMichael.Christensen@Sun.COM static void ds_handle_down_reset_events(ds_port_t *port);
150*7697SMichael.Christensen@Sun.COM static void ds_handle_recv(void *arg);
151*7697SMichael.Christensen@Sun.COM static void ds_dispatch_event(void *arg);
152*7697SMichael.Christensen@Sun.COM 
153*7697SMichael.Christensen@Sun.COM /* message sending functions */
154*7697SMichael.Christensen@Sun.COM static int ds_send_msg(ds_port_t *port, caddr_t msg, size_t msglen);
155*7697SMichael.Christensen@Sun.COM static int ds_send_reg_req(ds_svc_t *svc, ds_port_t *port);
156*7697SMichael.Christensen@Sun.COM static void ds_send_unreg_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl);
157*7697SMichael.Christensen@Sun.COM static void ds_send_data_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl);
158*7697SMichael.Christensen@Sun.COM 
159*7697SMichael.Christensen@Sun.COM /* walker functions */
160*7697SMichael.Christensen@Sun.COM static int ds_svc_isfree(ds_svc_t *svc, void *arg);
161*7697SMichael.Christensen@Sun.COM static int ds_svc_unregister(ds_svc_t *svc, void *arg);
162*7697SMichael.Christensen@Sun.COM static int ds_svc_port_up(ds_svc_t *svc, void *arg);
163*7697SMichael.Christensen@Sun.COM 
164*7697SMichael.Christensen@Sun.COM /* service utilities */
165*7697SMichael.Christensen@Sun.COM static void ds_reset_svc(ds_svc_t *svc, ds_port_t *port);
166*7697SMichael.Christensen@Sun.COM static int ds_svc_register_onport(ds_svc_t *svc, ds_port_t *port);
167*7697SMichael.Christensen@Sun.COM 
168*7697SMichael.Christensen@Sun.COM /* port utilities */
169*7697SMichael.Christensen@Sun.COM static void ds_port_reset(ds_port_t *port);
170*7697SMichael.Christensen@Sun.COM static ldc_status_t ds_update_ldc_state(ds_port_t *port);
171*7697SMichael.Christensen@Sun.COM 
172*7697SMichael.Christensen@Sun.COM /* misc utilities */
173*7697SMichael.Christensen@Sun.COM static void min_max_versions(int num_versions, ds_ver_t *sup_versionsp,
174*7697SMichael.Christensen@Sun.COM     uint16_t *min_major, uint16_t *max_major);
175*7697SMichael.Christensen@Sun.COM 
176*7697SMichael.Christensen@Sun.COM /* debug */
177*7697SMichael.Christensen@Sun.COM static char *decode_ldc_events(uint64_t event, char *buf);
178*7697SMichael.Christensen@Sun.COM 
179*7697SMichael.Christensen@Sun.COM /* loopback */
180*7697SMichael.Christensen@Sun.COM static void ds_loopback_register(ds_svc_hdl_t hdl);
181*7697SMichael.Christensen@Sun.COM static void ds_loopback_unregister(ds_svc_hdl_t hdl);
182*7697SMichael.Christensen@Sun.COM static void ds_loopback_send(ds_svc_hdl_t hdl, void *buf, size_t buflen);
183*7697SMichael.Christensen@Sun.COM static int ds_loopback_set_svc(ds_svc_t *svc, ds_svc_hdl_t lb_hdl);
184*7697SMichael.Christensen@Sun.COM 
185*7697SMichael.Christensen@Sun.COM /* client handling */
186*7697SMichael.Christensen@Sun.COM static int i_ds_hdl_lookup(char *service, uint_t is_client, ds_svc_hdl_t *hdlp,
187*7697SMichael.Christensen@Sun.COM     uint_t maxhdls);
188*7697SMichael.Christensen@Sun.COM static ds_svc_t *ds_find_clnt_svc_by_hdl_port(ds_svc_hdl_t hdl,
189*7697SMichael.Christensen@Sun.COM     ds_port_t *port);
190*7697SMichael.Christensen@Sun.COM static ds_svc_t *ds_find_svc_by_id_port(char *svc_id, int is_client,
191*7697SMichael.Christensen@Sun.COM     ds_port_t *port);
192*7697SMichael.Christensen@Sun.COM static ds_svc_t *ds_svc_clone(ds_svc_t *svc);
193*7697SMichael.Christensen@Sun.COM static void ds_portset_del_active_clients(char *service, ds_portset_t *portsp);
194*7697SMichael.Christensen@Sun.COM static void ds_check_for_dup_services(ds_svc_t *svc);
195*7697SMichael.Christensen@Sun.COM static void ds_delete_svc_entry(ds_svc_t *svc);
196*7697SMichael.Christensen@Sun.COM 
197*7697SMichael.Christensen@Sun.COM char *
198*7697SMichael.Christensen@Sun.COM ds_strdup(char *str)
199*7697SMichael.Christensen@Sun.COM {
200*7697SMichael.Christensen@Sun.COM 	char *newstr;
201*7697SMichael.Christensen@Sun.COM 
202*7697SMichael.Christensen@Sun.COM 	newstr = DS_MALLOC(strlen(str) + 1);
203*7697SMichael.Christensen@Sun.COM 	(void) strcpy(newstr, str);
204*7697SMichael.Christensen@Sun.COM 	return (newstr);
205*7697SMichael.Christensen@Sun.COM }
206*7697SMichael.Christensen@Sun.COM 
207*7697SMichael.Christensen@Sun.COM void
208*7697SMichael.Christensen@Sun.COM ds_common_init(void)
209*7697SMichael.Christensen@Sun.COM {
210*7697SMichael.Christensen@Sun.COM 	/* Validate version table */
211*7697SMichael.Christensen@Sun.COM 	ASSERT(ds_vers_isvalid(ds_vers, DS_NUM_VER) == DS_VERS_OK);
212*7697SMichael.Christensen@Sun.COM 
213*7697SMichael.Christensen@Sun.COM 	/* Initialize services table */
214*7697SMichael.Christensen@Sun.COM 	ds_init_svcs_tbl(DS_MAXSVCS_INIT);
215*7697SMichael.Christensen@Sun.COM 
216*7697SMichael.Christensen@Sun.COM 	/* enable callback processing */
217*7697SMichael.Christensen@Sun.COM 	ds_enabled = B_TRUE;
218*7697SMichael.Christensen@Sun.COM }
219*7697SMichael.Christensen@Sun.COM 
220*7697SMichael.Christensen@Sun.COM /* BEGIN LDC SUPPORT FUNCTIONS */
221*7697SMichael.Christensen@Sun.COM 
222*7697SMichael.Christensen@Sun.COM static char *
223*7697SMichael.Christensen@Sun.COM decode_ldc_events(uint64_t event, char *buf)
224*7697SMichael.Christensen@Sun.COM {
225*7697SMichael.Christensen@Sun.COM 	buf[0] = 0;
226*7697SMichael.Christensen@Sun.COM 	if (event & LDC_EVT_DOWN)	(void) strcat(buf, " DOWN");
227*7697SMichael.Christensen@Sun.COM 	if (event & LDC_EVT_RESET)	(void) strcat(buf, " RESET");
228*7697SMichael.Christensen@Sun.COM 	if (event & LDC_EVT_UP)		(void) strcat(buf, " UP");
229*7697SMichael.Christensen@Sun.COM 	if (event & LDC_EVT_READ)	(void) strcat(buf, " READ");
230*7697SMichael.Christensen@Sun.COM 	if (event & LDC_EVT_WRITE)	(void) strcat(buf, " WRITE");
231*7697SMichael.Christensen@Sun.COM 	return (buf);
232*7697SMichael.Christensen@Sun.COM }
233*7697SMichael.Christensen@Sun.COM 
234*7697SMichael.Christensen@Sun.COM static ldc_status_t
235*7697SMichael.Christensen@Sun.COM ds_update_ldc_state(ds_port_t *port)
236*7697SMichael.Christensen@Sun.COM {
237*7697SMichael.Christensen@Sun.COM 	ldc_status_t	ldc_state;
238*7697SMichael.Christensen@Sun.COM 	int		rv;
239*7697SMichael.Christensen@Sun.COM 	char		ebuf[DS_EBUFSIZE];
240*7697SMichael.Christensen@Sun.COM 
241*7697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&port->lock));
242*7697SMichael.Christensen@Sun.COM 
243*7697SMichael.Christensen@Sun.COM 	/*
244*7697SMichael.Christensen@Sun.COM 	 * Read status and update ldc state info in port structure.
245*7697SMichael.Christensen@Sun.COM 	 */
246*7697SMichael.Christensen@Sun.COM 	if ((rv = ldc_status(port->ldc.hdl, &ldc_state)) != 0) {
247*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_status error: %s" DS_EOL,
248*7697SMichael.Christensen@Sun.COM 		    PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
249*7697SMichael.Christensen@Sun.COM 		ldc_state = port->ldc.state;
250*7697SMichael.Christensen@Sun.COM 	} else {
251*7697SMichael.Christensen@Sun.COM 		port->ldc.state = ldc_state;
252*7697SMichael.Christensen@Sun.COM 	}
253*7697SMichael.Christensen@Sun.COM 
254*7697SMichael.Christensen@Sun.COM 	return (ldc_state);
255*7697SMichael.Christensen@Sun.COM }
256*7697SMichael.Christensen@Sun.COM 
257*7697SMichael.Christensen@Sun.COM static void
258*7697SMichael.Christensen@Sun.COM ds_handle_down_reset_events(ds_port_t *port)
259*7697SMichael.Christensen@Sun.COM {
260*7697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: entered" DS_EOL, PORTID(port),
261*7697SMichael.Christensen@Sun.COM 	    __func__);
262*7697SMichael.Christensen@Sun.COM 
263*7697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
264*7697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
265*7697SMichael.Christensen@Sun.COM 
266*7697SMichael.Christensen@Sun.COM 	ds_sys_drain_events(port);
267*7697SMichael.Christensen@Sun.COM 
268*7697SMichael.Christensen@Sun.COM 	(void) ds_update_ldc_state(port);
269*7697SMichael.Christensen@Sun.COM 
270*7697SMichael.Christensen@Sun.COM 	/* reset the port state */
271*7697SMichael.Christensen@Sun.COM 	ds_port_reset(port);
272*7697SMichael.Christensen@Sun.COM 
273*7697SMichael.Christensen@Sun.COM 	/* acknowledge the reset */
274*7697SMichael.Christensen@Sun.COM 	(void) ldc_up(port->ldc.hdl);
275*7697SMichael.Christensen@Sun.COM 
276*7697SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
277*7697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
278*7697SMichael.Christensen@Sun.COM 
279*7697SMichael.Christensen@Sun.COM 	ds_handle_up_event(port);
280*7697SMichael.Christensen@Sun.COM 
281*7697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: exit" DS_EOL, PORTID(port), __func__);
282*7697SMichael.Christensen@Sun.COM }
283*7697SMichael.Christensen@Sun.COM 
284*7697SMichael.Christensen@Sun.COM static void
285*7697SMichael.Christensen@Sun.COM ds_handle_up_event(ds_port_t *port)
286*7697SMichael.Christensen@Sun.COM {
287*7697SMichael.Christensen@Sun.COM 	ldc_status_t	ldc_state;
288*7697SMichael.Christensen@Sun.COM 
289*7697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: entered" DS_EOL, PORTID(port),
290*7697SMichael.Christensen@Sun.COM 	    __func__);
291*7697SMichael.Christensen@Sun.COM 
292*7697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
293*7697SMichael.Christensen@Sun.COM 
294*7697SMichael.Christensen@Sun.COM 	ldc_state = ds_update_ldc_state(port);
295*7697SMichael.Christensen@Sun.COM 
296*7697SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
297*7697SMichael.Christensen@Sun.COM 
298*7697SMichael.Christensen@Sun.COM 	if ((ldc_state == LDC_UP) && IS_DS_PORT(port)) {
299*7697SMichael.Christensen@Sun.COM 		/*
300*7697SMichael.Christensen@Sun.COM 		 * Initiate the handshake.
301*7697SMichael.Christensen@Sun.COM 		 */
302*7697SMichael.Christensen@Sun.COM 		ds_send_init_req(port);
303*7697SMichael.Christensen@Sun.COM 	}
304*7697SMichael.Christensen@Sun.COM 
305*7697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: exit" DS_EOL, PORTID(port), __func__);
306*7697SMichael.Christensen@Sun.COM }
307*7697SMichael.Christensen@Sun.COM 
308*7697SMichael.Christensen@Sun.COM static uint_t
309*7697SMichael.Christensen@Sun.COM ds_ldc_cb(uint64_t event, caddr_t arg)
310*7697SMichael.Christensen@Sun.COM {
311*7697SMichael.Christensen@Sun.COM 	ds_port_t	*port = (ds_port_t *)arg;
312*7697SMichael.Christensen@Sun.COM 	char		evstring[DS_DECODE_BUF_LEN];
313*7697SMichael.Christensen@Sun.COM 
314*7697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: %s event (%llx) received" DS_EOL,
315*7697SMichael.Christensen@Sun.COM 	    PORTID(port), __func__, decode_ldc_events(event, evstring),
316*7697SMichael.Christensen@Sun.COM 	    (u_longlong_t)event);
317*7697SMichael.Christensen@Sun.COM 
318*7697SMichael.Christensen@Sun.COM 	if (!ds_enabled) {
319*7697SMichael.Christensen@Sun.COM 		DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: callback handling is disabled"
320*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), __func__);
321*7697SMichael.Christensen@Sun.COM 		return (LDC_SUCCESS);
322*7697SMichael.Christensen@Sun.COM 	}
323*7697SMichael.Christensen@Sun.COM 
324*7697SMichael.Christensen@Sun.COM 	if (event & (LDC_EVT_DOWN | LDC_EVT_RESET)) {
325*7697SMichael.Christensen@Sun.COM 		ds_handle_down_reset_events(port);
326*7697SMichael.Christensen@Sun.COM 		goto done;
327*7697SMichael.Christensen@Sun.COM 	}
328*7697SMichael.Christensen@Sun.COM 
329*7697SMichael.Christensen@Sun.COM 	if (event & LDC_EVT_UP) {
330*7697SMichael.Christensen@Sun.COM 		ds_handle_up_event(port);
331*7697SMichael.Christensen@Sun.COM 	}
332*7697SMichael.Christensen@Sun.COM 
333*7697SMichael.Christensen@Sun.COM 	if (event & LDC_EVT_READ) {
334*7697SMichael.Christensen@Sun.COM 		if (port->ldc.state != LDC_UP) {
335*7697SMichael.Christensen@Sun.COM 			cmn_err(CE_WARN, "ds@%lx: %s: LDC READ event while "
336*7697SMichael.Christensen@Sun.COM 			    "port not up" DS_EOL, PORTID(port), __func__);
337*7697SMichael.Christensen@Sun.COM 			goto done;
338*7697SMichael.Christensen@Sun.COM 		}
339*7697SMichael.Christensen@Sun.COM 
340*7697SMichael.Christensen@Sun.COM 		if (ds_sys_dispatch_func(ds_handle_recv, port)) {
341*7697SMichael.Christensen@Sun.COM 			cmn_err(CE_WARN, "ds@%lx: error initiating LDC READ "
342*7697SMichael.Christensen@Sun.COM 			    " event", PORTID(port));
343*7697SMichael.Christensen@Sun.COM 		}
344*7697SMichael.Christensen@Sun.COM 	}
345*7697SMichael.Christensen@Sun.COM 
346*7697SMichael.Christensen@Sun.COM 	if (event & LDC_EVT_WRITE) {
347*7697SMichael.Christensen@Sun.COM 		DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: LDC WRITE event received, "
348*7697SMichael.Christensen@Sun.COM 		    "not supported" DS_EOL, PORTID(port), __func__);
349*7697SMichael.Christensen@Sun.COM 	}
350*7697SMichael.Christensen@Sun.COM 
351*7697SMichael.Christensen@Sun.COM 	if (event & ~(LDC_EVT_UP | LDC_EVT_READ)) {
352*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: %s: Unexpected LDC event received: "
353*7697SMichael.Christensen@Sun.COM 		    "0x%llx" DS_EOL, PORTID(port), __func__,
354*7697SMichael.Christensen@Sun.COM 		    (u_longlong_t)event);
355*7697SMichael.Christensen@Sun.COM 	}
356*7697SMichael.Christensen@Sun.COM done:
357*7697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: exit" DS_EOL, PORTID(port), __func__);
358*7697SMichael.Christensen@Sun.COM 
359*7697SMichael.Christensen@Sun.COM 	return (LDC_SUCCESS);
360*7697SMichael.Christensen@Sun.COM }
361*7697SMichael.Christensen@Sun.COM 
362*7697SMichael.Christensen@Sun.COM static int
363*7697SMichael.Christensen@Sun.COM ds_ldc_init(ds_port_t *port)
364*7697SMichael.Christensen@Sun.COM {
365*7697SMichael.Christensen@Sun.COM 	int		rv;
366*7697SMichael.Christensen@Sun.COM 	ldc_attr_t	ldc_attr;
367*7697SMichael.Christensen@Sun.COM 	caddr_t		ldc_cb_arg = (caddr_t)port;
368*7697SMichael.Christensen@Sun.COM 	char		ebuf[DS_EBUFSIZE];
369*7697SMichael.Christensen@Sun.COM 
370*7697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&port->lock));
371*7697SMichael.Christensen@Sun.COM 
372*7697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: ldc_id=%lld" DS_EOL,
373*7697SMichael.Christensen@Sun.COM 	    PORTID(port), __func__, (u_longlong_t)port->ldc.id);
374*7697SMichael.Christensen@Sun.COM 
375*7697SMichael.Christensen@Sun.COM 	ldc_attr.devclass = LDC_DEV_GENERIC;
376*7697SMichael.Christensen@Sun.COM 	ldc_attr.instance = 0;
377*7697SMichael.Christensen@Sun.COM 	ldc_attr.mode = LDC_MODE_RELIABLE;
378*7697SMichael.Christensen@Sun.COM 	ldc_attr.mtu = DS_STREAM_MTU;
379*7697SMichael.Christensen@Sun.COM 
380*7697SMichael.Christensen@Sun.COM 	if ((rv = ldc_init(port->ldc.id, &ldc_attr, &port->ldc.hdl)) != 0) {
381*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_id: %lx, ldc_init error: %s"
382*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), __func__, port->ldc.id,
383*7697SMichael.Christensen@Sun.COM 		    ds_errno_to_str(rv, ebuf));
384*7697SMichael.Christensen@Sun.COM 		return (rv);
385*7697SMichael.Christensen@Sun.COM 	}
386*7697SMichael.Christensen@Sun.COM 
387*7697SMichael.Christensen@Sun.COM 	rv = ldc_reg_callback(port->ldc.hdl, ds_ldc_cb, ldc_cb_arg);
388*7697SMichael.Christensen@Sun.COM 	if (rv != 0) {
389*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_reg_callback error: %s"
390*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
391*7697SMichael.Christensen@Sun.COM 		return (rv);
392*7697SMichael.Christensen@Sun.COM 	}
393*7697SMichael.Christensen@Sun.COM 
394*7697SMichael.Christensen@Sun.COM 	ds_sys_ldc_init(port);
395*7697SMichael.Christensen@Sun.COM 	return (0);
396*7697SMichael.Christensen@Sun.COM }
397*7697SMichael.Christensen@Sun.COM 
398*7697SMichael.Christensen@Sun.COM int
399*7697SMichael.Christensen@Sun.COM ds_ldc_fini(ds_port_t *port)
400*7697SMichael.Christensen@Sun.COM {
401*7697SMichael.Christensen@Sun.COM 	int	rv;
402*7697SMichael.Christensen@Sun.COM 	char	ebuf[DS_EBUFSIZE];
403*7697SMichael.Christensen@Sun.COM 
404*7697SMichael.Christensen@Sun.COM 	ASSERT(port->state >= DS_PORT_LDC_INIT);
405*7697SMichael.Christensen@Sun.COM 
406*7697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: ldc_id=%ld" DS_EOL, PORTID(port),
407*7697SMichael.Christensen@Sun.COM 	    __func__, port->ldc.id);
408*7697SMichael.Christensen@Sun.COM 
409*7697SMichael.Christensen@Sun.COM 	if ((rv = ldc_close(port->ldc.hdl)) != 0) {
410*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_close error: %s" DS_EOL,
411*7697SMichael.Christensen@Sun.COM 		    PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
412*7697SMichael.Christensen@Sun.COM 		return (rv);
413*7697SMichael.Christensen@Sun.COM 	}
414*7697SMichael.Christensen@Sun.COM 
415*7697SMichael.Christensen@Sun.COM 	if ((rv = ldc_unreg_callback(port->ldc.hdl)) != 0) {
416*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_unreg_callback error: %s"
417*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
418*7697SMichael.Christensen@Sun.COM 		return (rv);
419*7697SMichael.Christensen@Sun.COM 	}
420*7697SMichael.Christensen@Sun.COM 
421*7697SMichael.Christensen@Sun.COM 	if ((rv = ldc_fini(port->ldc.hdl)) != 0) {
422*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: %s: ldc_fini error: %s" DS_EOL,
423*7697SMichael.Christensen@Sun.COM 		    PORTID(port), __func__, ds_errno_to_str(rv, ebuf));
424*7697SMichael.Christensen@Sun.COM 		return (rv);
425*7697SMichael.Christensen@Sun.COM 	}
426*7697SMichael.Christensen@Sun.COM 
427*7697SMichael.Christensen@Sun.COM 	return (rv);
428*7697SMichael.Christensen@Sun.COM }
429*7697SMichael.Christensen@Sun.COM 
430*7697SMichael.Christensen@Sun.COM /*
431*7697SMichael.Christensen@Sun.COM  * Attempt to read a specified number of bytes from a particular LDC.
432*7697SMichael.Christensen@Sun.COM  * Returns zero for success or the return code from the LDC read on
433*7697SMichael.Christensen@Sun.COM  * failure. The actual number of bytes read from the LDC is returned
434*7697SMichael.Christensen@Sun.COM  * in the size parameter.
435*7697SMichael.Christensen@Sun.COM  */
436*7697SMichael.Christensen@Sun.COM static int
437*7697SMichael.Christensen@Sun.COM ds_recv_msg(ds_port_t *port, caddr_t msgp, size_t *sizep)
438*7697SMichael.Christensen@Sun.COM {
439*7697SMichael.Christensen@Sun.COM 	int	rv = 0;
440*7697SMichael.Christensen@Sun.COM 	size_t	bytes_req = *sizep;
441*7697SMichael.Christensen@Sun.COM 	size_t	bytes_left = bytes_req;
442*7697SMichael.Christensen@Sun.COM 	size_t	nbytes;
443*7697SMichael.Christensen@Sun.COM 	int	retry_count = 0;
444*7697SMichael.Christensen@Sun.COM 	char	ebuf[DS_EBUFSIZE];
445*7697SMichael.Christensen@Sun.COM 
446*7697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&port->rcv_lock));
447*7697SMichael.Christensen@Sun.COM 
448*7697SMichael.Christensen@Sun.COM 	*sizep = 0;
449*7697SMichael.Christensen@Sun.COM 
450*7697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: attempting to read %ld bytes" DS_EOL,
451*7697SMichael.Christensen@Sun.COM 	    PORTID(port), bytes_req);
452*7697SMichael.Christensen@Sun.COM 
453*7697SMichael.Christensen@Sun.COM 	while (bytes_left > 0) {
454*7697SMichael.Christensen@Sun.COM 
455*7697SMichael.Christensen@Sun.COM 		nbytes = bytes_left;
456*7697SMichael.Christensen@Sun.COM 
457*7697SMichael.Christensen@Sun.COM 		if ((rv = ldc_read(port->ldc.hdl, msgp, &nbytes)) != 0) {
458*7697SMichael.Christensen@Sun.COM 			if (rv == ECONNRESET) {
459*7697SMichael.Christensen@Sun.COM 				break;
460*7697SMichael.Christensen@Sun.COM 			} else if (rv != EAGAIN) {
461*7697SMichael.Christensen@Sun.COM 				cmn_err(CE_NOTE, "ds@%lx: %s: %s" DS_EOL,
462*7697SMichael.Christensen@Sun.COM 				    PORTID(port), __func__,
463*7697SMichael.Christensen@Sun.COM 				    ds_errno_to_str(rv, ebuf));
464*7697SMichael.Christensen@Sun.COM 				break;
465*7697SMichael.Christensen@Sun.COM 			}
466*7697SMichael.Christensen@Sun.COM 		} else {
467*7697SMichael.Christensen@Sun.COM 			if (nbytes != 0) {
468*7697SMichael.Christensen@Sun.COM 				DS_DBG_LDC(CE_NOTE, "ds@%lx: "
469*7697SMichael.Christensen@Sun.COM 				    "read %ld bytes, %d retries" DS_EOL,
470*7697SMichael.Christensen@Sun.COM 				    PORTID(port), nbytes, retry_count);
471*7697SMichael.Christensen@Sun.COM 
472*7697SMichael.Christensen@Sun.COM 				*sizep += nbytes;
473*7697SMichael.Christensen@Sun.COM 				msgp += nbytes;
474*7697SMichael.Christensen@Sun.COM 				bytes_left -= nbytes;
475*7697SMichael.Christensen@Sun.COM 
476*7697SMichael.Christensen@Sun.COM 				/* reset counter on a successful read */
477*7697SMichael.Christensen@Sun.COM 				retry_count = 0;
478*7697SMichael.Christensen@Sun.COM 				continue;
479*7697SMichael.Christensen@Sun.COM 			}
480*7697SMichael.Christensen@Sun.COM 
481*7697SMichael.Christensen@Sun.COM 			/*
482*7697SMichael.Christensen@Sun.COM 			 * No data was read. Check if this is the
483*7697SMichael.Christensen@Sun.COM 			 * first attempt. If so, just return since
484*7697SMichael.Christensen@Sun.COM 			 * nothing has been read yet.
485*7697SMichael.Christensen@Sun.COM 			 */
486*7697SMichael.Christensen@Sun.COM 			if (bytes_left == bytes_req) {
487*7697SMichael.Christensen@Sun.COM 				DS_DBG_LDC(CE_NOTE, "ds@%lx: read zero bytes, "
488*7697SMichael.Christensen@Sun.COM 				    " no data available" DS_EOL, PORTID(port));
489*7697SMichael.Christensen@Sun.COM 				break;
490*7697SMichael.Christensen@Sun.COM 			}
491*7697SMichael.Christensen@Sun.COM 		}
492*7697SMichael.Christensen@Sun.COM 
493*7697SMichael.Christensen@Sun.COM 		/*
494*7697SMichael.Christensen@Sun.COM 		 * A retry is necessary because the read returned
495*7697SMichael.Christensen@Sun.COM 		 * EAGAIN, or a zero length read occurred after
496*7697SMichael.Christensen@Sun.COM 		 * reading a partial message.
497*7697SMichael.Christensen@Sun.COM 		 */
498*7697SMichael.Christensen@Sun.COM 		if (retry_count++ >= ds_retries) {
499*7697SMichael.Christensen@Sun.COM 			DS_DBG_LDC(CE_NOTE, "ds@%lx: timed out waiting for "
500*7697SMichael.Christensen@Sun.COM 			    "message" DS_EOL, PORTID(port));
501*7697SMichael.Christensen@Sun.COM 			break;
502*7697SMichael.Christensen@Sun.COM 		}
503*7697SMichael.Christensen@Sun.COM 
504*7697SMichael.Christensen@Sun.COM 		drv_usecwait(ds_delay);
505*7697SMichael.Christensen@Sun.COM 	}
506*7697SMichael.Christensen@Sun.COM 
507*7697SMichael.Christensen@Sun.COM 	return (rv);
508*7697SMichael.Christensen@Sun.COM }
509*7697SMichael.Christensen@Sun.COM 
510*7697SMichael.Christensen@Sun.COM static void
511*7697SMichael.Christensen@Sun.COM ds_handle_recv(void *arg)
512*7697SMichael.Christensen@Sun.COM {
513*7697SMichael.Christensen@Sun.COM 	ds_port_t	*port = (ds_port_t *)arg;
514*7697SMichael.Christensen@Sun.COM 	char		*hbuf;
515*7697SMichael.Christensen@Sun.COM 	size_t		msglen;
516*7697SMichael.Christensen@Sun.COM 	size_t		read_size;
517*7697SMichael.Christensen@Sun.COM 	boolean_t	hasdata;
518*7697SMichael.Christensen@Sun.COM 	ds_hdr_t	hdr;
519*7697SMichael.Christensen@Sun.COM 	uint8_t		*msg;
520*7697SMichael.Christensen@Sun.COM 	char		*currp;
521*7697SMichael.Christensen@Sun.COM 	int		rv;
522*7697SMichael.Christensen@Sun.COM 	ds_event_t	*devent;
523*7697SMichael.Christensen@Sun.COM 
524*7697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s..." DS_EOL, PORTID(port), __func__);
525*7697SMichael.Christensen@Sun.COM 
526*7697SMichael.Christensen@Sun.COM 	/*
527*7697SMichael.Christensen@Sun.COM 	 * Read messages from the channel until there are none
528*7697SMichael.Christensen@Sun.COM 	 * pending. Valid messages are dispatched to be handled
529*7697SMichael.Christensen@Sun.COM 	 * by a separate thread while any malformed messages are
530*7697SMichael.Christensen@Sun.COM 	 * dropped.
531*7697SMichael.Christensen@Sun.COM 	 */
532*7697SMichael.Christensen@Sun.COM 
533*7697SMichael.Christensen@Sun.COM 	mutex_enter(&port->rcv_lock);
534*7697SMichael.Christensen@Sun.COM 
535*7697SMichael.Christensen@Sun.COM 	while (((rv = ldc_chkq(port->ldc.hdl, &hasdata)) == 0) && hasdata) {
536*7697SMichael.Christensen@Sun.COM 
537*7697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "ds@%lx: %s: reading next message" DS_EOL,
538*7697SMichael.Christensen@Sun.COM 		    PORTID(port), __func__);
539*7697SMichael.Christensen@Sun.COM 
540*7697SMichael.Christensen@Sun.COM 		/*
541*7697SMichael.Christensen@Sun.COM 		 * Read in the next message.
542*7697SMichael.Christensen@Sun.COM 		 */
543*7697SMichael.Christensen@Sun.COM 		hbuf = (char *)&hdr;
544*7697SMichael.Christensen@Sun.COM 		bzero(hbuf, DS_HDR_SZ);
545*7697SMichael.Christensen@Sun.COM 		read_size = DS_HDR_SZ;
546*7697SMichael.Christensen@Sun.COM 		currp = hbuf;
547*7697SMichael.Christensen@Sun.COM 
548*7697SMichael.Christensen@Sun.COM 		/* read in the message header */
549*7697SMichael.Christensen@Sun.COM 		if ((rv = ds_recv_msg(port, currp, &read_size)) != 0) {
550*7697SMichael.Christensen@Sun.COM 			break;
551*7697SMichael.Christensen@Sun.COM 		}
552*7697SMichael.Christensen@Sun.COM 
553*7697SMichael.Christensen@Sun.COM 		if (read_size < DS_HDR_SZ) {
554*7697SMichael.Christensen@Sun.COM 			/*
555*7697SMichael.Christensen@Sun.COM 			 * A zero length read is a valid signal that
556*7697SMichael.Christensen@Sun.COM 			 * there is no data left on the channel.
557*7697SMichael.Christensen@Sun.COM 			 */
558*7697SMichael.Christensen@Sun.COM 			if (read_size != 0) {
559*7697SMichael.Christensen@Sun.COM 				cmn_err(CE_WARN, "ds@%lx: invalid message "
560*7697SMichael.Christensen@Sun.COM 				    "length, received %ld bytes, expected %ld"
561*7697SMichael.Christensen@Sun.COM 				    DS_EOL, PORTID(port), read_size, DS_HDR_SZ);
562*7697SMichael.Christensen@Sun.COM 			}
563*7697SMichael.Christensen@Sun.COM 			continue;
564*7697SMichael.Christensen@Sun.COM 		}
565*7697SMichael.Christensen@Sun.COM 
566*7697SMichael.Christensen@Sun.COM 		/* get payload size and allocate a buffer */
567*7697SMichael.Christensen@Sun.COM 		read_size = ((ds_hdr_t *)hbuf)->payload_len;
568*7697SMichael.Christensen@Sun.COM 		msglen = DS_HDR_SZ + read_size;
569*7697SMichael.Christensen@Sun.COM 		msg = DS_MALLOC(msglen);
570*7697SMichael.Christensen@Sun.COM 		if (!msg) {
571*7697SMichael.Christensen@Sun.COM 			cmn_err(CE_WARN, "Memory allocation failed attempting "
572*7697SMichael.Christensen@Sun.COM 			    " to allocate %d bytes." DS_EOL, (int)msglen);
573*7697SMichael.Christensen@Sun.COM 			continue;
574*7697SMichael.Christensen@Sun.COM 		}
575*7697SMichael.Christensen@Sun.COM 
576*7697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "ds@%lx: %s: message payload len %d" DS_EOL,
577*7697SMichael.Christensen@Sun.COM 		    PORTID(port), __func__, (int)read_size);
578*7697SMichael.Christensen@Sun.COM 
579*7697SMichael.Christensen@Sun.COM 		/* move message header into buffer */
580*7697SMichael.Christensen@Sun.COM 		(void) memcpy(msg, hbuf, DS_HDR_SZ);
581*7697SMichael.Christensen@Sun.COM 		currp = (char *)(msg) + DS_HDR_SZ;
582*7697SMichael.Christensen@Sun.COM 
583*7697SMichael.Christensen@Sun.COM 		/* read in the message body */
584*7697SMichael.Christensen@Sun.COM 		if ((rv = ds_recv_msg(port, currp, &read_size)) != 0) {
585*7697SMichael.Christensen@Sun.COM 			DS_FREE(msg, msglen);
586*7697SMichael.Christensen@Sun.COM 			break;
587*7697SMichael.Christensen@Sun.COM 		}
588*7697SMichael.Christensen@Sun.COM 
589*7697SMichael.Christensen@Sun.COM 		/* validate the size of the message */
590*7697SMichael.Christensen@Sun.COM 		if ((DS_HDR_SZ + read_size) != msglen) {
591*7697SMichael.Christensen@Sun.COM 			cmn_err(CE_WARN, "ds@%lx: %s: invalid message length, "
592*7697SMichael.Christensen@Sun.COM 			    "received %ld bytes, expected %ld" DS_EOL,
593*7697SMichael.Christensen@Sun.COM 			    PORTID(port), __func__, (DS_HDR_SZ + read_size),
594*7697SMichael.Christensen@Sun.COM 			    msglen);
595*7697SMichael.Christensen@Sun.COM 			DS_FREE(msg, msglen);
596*7697SMichael.Christensen@Sun.COM 			continue;
597*7697SMichael.Christensen@Sun.COM 		}
598*7697SMichael.Christensen@Sun.COM 
599*7697SMichael.Christensen@Sun.COM 		DS_DUMP_MSG(DS_DBG_FLAG_LDC, msg, msglen);
600*7697SMichael.Christensen@Sun.COM 
601*7697SMichael.Christensen@Sun.COM 		/*
602*7697SMichael.Christensen@Sun.COM 		 * Send the message for processing, and store it
603*7697SMichael.Christensen@Sun.COM 		 * in the log. The memory is deallocated only when
604*7697SMichael.Christensen@Sun.COM 		 * the message is removed from the log.
605*7697SMichael.Christensen@Sun.COM 		 */
606*7697SMichael.Christensen@Sun.COM 
607*7697SMichael.Christensen@Sun.COM 		devent = DS_MALLOC(sizeof (ds_event_t));
608*7697SMichael.Christensen@Sun.COM 		devent->port = port;
609*7697SMichael.Christensen@Sun.COM 		devent->buf = (char *)msg;
610*7697SMichael.Christensen@Sun.COM 		devent->buflen = msglen;
611*7697SMichael.Christensen@Sun.COM 
612*7697SMichael.Christensen@Sun.COM 		/* log the message */
613*7697SMichael.Christensen@Sun.COM 		(void) ds_log_add_msg(DS_LOG_IN(port->id), msg, msglen);
614*7697SMichael.Christensen@Sun.COM 
615*7697SMichael.Christensen@Sun.COM 		if (ds_sys_dispatch_func(ds_dispatch_event, devent)) {
616*7697SMichael.Christensen@Sun.COM 			cmn_err(CE_WARN, "ds@%lx: error initiating "
617*7697SMichael.Christensen@Sun.COM 			    "event handler", PORTID(port));
618*7697SMichael.Christensen@Sun.COM 			DS_FREE(devent, sizeof (ds_event_t));
619*7697SMichael.Christensen@Sun.COM 		}
620*7697SMichael.Christensen@Sun.COM 	}
621*7697SMichael.Christensen@Sun.COM 
622*7697SMichael.Christensen@Sun.COM 	mutex_exit(&port->rcv_lock);
623*7697SMichael.Christensen@Sun.COM 
624*7697SMichael.Christensen@Sun.COM 	/* handle connection reset errors returned from ds_recv_msg */
625*7697SMichael.Christensen@Sun.COM 	if (rv == ECONNRESET) {
626*7697SMichael.Christensen@Sun.COM 		ds_handle_down_reset_events(port);
627*7697SMichael.Christensen@Sun.COM 	}
628*7697SMichael.Christensen@Sun.COM 
629*7697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s done" DS_EOL, PORTID(port), __func__);
630*7697SMichael.Christensen@Sun.COM }
631*7697SMichael.Christensen@Sun.COM 
632*7697SMichael.Christensen@Sun.COM static void
633*7697SMichael.Christensen@Sun.COM ds_dispatch_event(void *arg)
634*7697SMichael.Christensen@Sun.COM {
635*7697SMichael.Christensen@Sun.COM 	ds_event_t	*event = (ds_event_t *)arg;
636*7697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
637*7697SMichael.Christensen@Sun.COM 	ds_port_t	*port;
638*7697SMichael.Christensen@Sun.COM 
639*7697SMichael.Christensen@Sun.COM 	port = event->port;
640*7697SMichael.Christensen@Sun.COM 
641*7697SMichael.Christensen@Sun.COM 	hdr = (ds_hdr_t *)event->buf;
642*7697SMichael.Christensen@Sun.COM 
643*7697SMichael.Christensen@Sun.COM 	if (DS_MSG_TYPE_VALID(hdr->msg_type)) {
644*7697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "ds@%lx: dispatch_event: msg_type=%d" DS_EOL,
645*7697SMichael.Christensen@Sun.COM 		    PORTID(port), hdr->msg_type);
646*7697SMichael.Christensen@Sun.COM 
647*7697SMichael.Christensen@Sun.COM 		(*ds_msg_handlers[hdr->msg_type])(port, event->buf,
648*7697SMichael.Christensen@Sun.COM 		    event->buflen);
649*7697SMichael.Christensen@Sun.COM 	} else {
650*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: dispatch_event: invalid msg "
651*7697SMichael.Christensen@Sun.COM 		    "type (%d)" DS_EOL, PORTID(port), hdr->msg_type);
652*7697SMichael.Christensen@Sun.COM 	}
653*7697SMichael.Christensen@Sun.COM 
654*7697SMichael.Christensen@Sun.COM 	DS_FREE(event->buf, event->buflen);
655*7697SMichael.Christensen@Sun.COM 	DS_FREE(event, sizeof (ds_event_t));
656*7697SMichael.Christensen@Sun.COM }
657*7697SMichael.Christensen@Sun.COM 
658*7697SMichael.Christensen@Sun.COM int
659*7697SMichael.Christensen@Sun.COM ds_send_msg(ds_port_t *port, caddr_t msg, size_t msglen)
660*7697SMichael.Christensen@Sun.COM {
661*7697SMichael.Christensen@Sun.COM 	int	rv;
662*7697SMichael.Christensen@Sun.COM 	caddr_t	currp = msg;
663*7697SMichael.Christensen@Sun.COM 	size_t	amt_left = msglen;
664*7697SMichael.Christensen@Sun.COM 	int	loopcnt = 0;
665*7697SMichael.Christensen@Sun.COM 
666*7697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s msglen: %ld" DS_EOL, PORTID(port),
667*7697SMichael.Christensen@Sun.COM 	    __func__, msglen);
668*7697SMichael.Christensen@Sun.COM 	DS_DUMP_MSG(DS_DBG_FLAG_LDC, msg, msglen);
669*7697SMichael.Christensen@Sun.COM 
670*7697SMichael.Christensen@Sun.COM 	/*
671*7697SMichael.Christensen@Sun.COM 	 * Ensure that no other messages can be sent on this port by holding
672*7697SMichael.Christensen@Sun.COM 	 * the tx_lock mutex in case the write doesn't get sent with one write.
673*7697SMichael.Christensen@Sun.COM 	 * This guarantees that the message doesn't become fragmented.
674*7697SMichael.Christensen@Sun.COM 	 */
675*7697SMichael.Christensen@Sun.COM 	mutex_enter(&port->tx_lock);
676*7697SMichael.Christensen@Sun.COM 
677*7697SMichael.Christensen@Sun.COM 	do {
678*7697SMichael.Christensen@Sun.COM 		if ((rv = ldc_write(port->ldc.hdl, currp, &msglen)) != 0) {
679*7697SMichael.Christensen@Sun.COM 			if (rv == ECONNRESET) {
680*7697SMichael.Christensen@Sun.COM 				mutex_exit(&port->tx_lock);
681*7697SMichael.Christensen@Sun.COM 				ds_handle_down_reset_events(port);
682*7697SMichael.Christensen@Sun.COM 				return (rv);
683*7697SMichael.Christensen@Sun.COM 			} else if ((rv == EWOULDBLOCK) &&
684*7697SMichael.Christensen@Sun.COM 			    (loopcnt++ < ds_retries)) {
685*7697SMichael.Christensen@Sun.COM 				drv_usecwait(ds_delay);
686*7697SMichael.Christensen@Sun.COM 			} else {
687*7697SMichael.Christensen@Sun.COM 				cmn_err(CE_WARN, "ds@%lx: send_msg: ldc_write "
688*7697SMichael.Christensen@Sun.COM 				    "failed (%d), %d bytes remaining" DS_EOL,
689*7697SMichael.Christensen@Sun.COM 				    PORTID(port), rv, (int)amt_left);
690*7697SMichael.Christensen@Sun.COM 				goto error;
691*7697SMichael.Christensen@Sun.COM 			}
692*7697SMichael.Christensen@Sun.COM 		} else {
693*7697SMichael.Christensen@Sun.COM 			amt_left -= msglen;
694*7697SMichael.Christensen@Sun.COM 			currp += msglen;
695*7697SMichael.Christensen@Sun.COM 			msglen = amt_left;
696*7697SMichael.Christensen@Sun.COM 			loopcnt = 0;
697*7697SMichael.Christensen@Sun.COM 		}
698*7697SMichael.Christensen@Sun.COM 	} while (amt_left > 0);
699*7697SMichael.Christensen@Sun.COM error:
700*7697SMichael.Christensen@Sun.COM 	mutex_exit(&port->tx_lock);
701*7697SMichael.Christensen@Sun.COM 
702*7697SMichael.Christensen@Sun.COM 	return (rv);
703*7697SMichael.Christensen@Sun.COM }
704*7697SMichael.Christensen@Sun.COM 
705*7697SMichael.Christensen@Sun.COM /* END LDC SUPPORT FUNCTIONS */
706*7697SMichael.Christensen@Sun.COM 
707*7697SMichael.Christensen@Sun.COM 
708*7697SMichael.Christensen@Sun.COM /* BEGIN DS PROTOCOL SUPPORT FUNCTIONS */
709*7697SMichael.Christensen@Sun.COM 
710*7697SMichael.Christensen@Sun.COM static void
711*7697SMichael.Christensen@Sun.COM ds_handle_init_req(ds_port_t *port, caddr_t buf, size_t len)
712*7697SMichael.Christensen@Sun.COM {
713*7697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
714*7697SMichael.Christensen@Sun.COM 	ds_init_ack_t	*ack;
715*7697SMichael.Christensen@Sun.COM 	ds_init_nack_t	*nack;
716*7697SMichael.Christensen@Sun.COM 	char		*msg;
717*7697SMichael.Christensen@Sun.COM 	size_t		msglen;
718*7697SMichael.Christensen@Sun.COM 	ds_init_req_t	*req;
719*7697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_init_req_t);
720*7697SMichael.Christensen@Sun.COM 	uint16_t	new_major;
721*7697SMichael.Christensen@Sun.COM 	uint16_t	new_minor;
722*7697SMichael.Christensen@Sun.COM 	boolean_t	match;
723*7697SMichael.Christensen@Sun.COM 
724*7697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
725*7697SMichael.Christensen@Sun.COM 	if (len != explen) {
726*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <init_req: invalid message "
727*7697SMichael.Christensen@Sun.COM 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
728*7697SMichael.Christensen@Sun.COM 		    explen);
729*7697SMichael.Christensen@Sun.COM 		return;
730*7697SMichael.Christensen@Sun.COM 	}
731*7697SMichael.Christensen@Sun.COM 
732*7697SMichael.Christensen@Sun.COM 	req = (ds_init_req_t *)(buf + DS_HDR_SZ);
733*7697SMichael.Christensen@Sun.COM 
734*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_req: ver=%d.%d" DS_EOL,
735*7697SMichael.Christensen@Sun.COM 	    PORTID(port), req->major_vers, req->minor_vers);
736*7697SMichael.Christensen@Sun.COM 
737*7697SMichael.Christensen@Sun.COM 	match = negotiate_version(DS_NUM_VER, &ds_vers[0],
738*7697SMichael.Christensen@Sun.COM 	    req->major_vers, &new_major, &new_minor);
739*7697SMichael.Christensen@Sun.COM 
740*7697SMichael.Christensen@Sun.COM 	/*
741*7697SMichael.Christensen@Sun.COM 	 * Check version info. ACK only if the major numbers exactly
742*7697SMichael.Christensen@Sun.COM 	 * match. The service entity can retry with a new minor
743*7697SMichael.Christensen@Sun.COM 	 * based on the response sent as part of the NACK.
744*7697SMichael.Christensen@Sun.COM 	 */
745*7697SMichael.Christensen@Sun.COM 	if (match) {
746*7697SMichael.Christensen@Sun.COM 		msglen = DS_MSG_LEN(ds_init_ack_t);
747*7697SMichael.Christensen@Sun.COM 		msg = DS_MALLOC(msglen);
748*7697SMichael.Christensen@Sun.COM 
749*7697SMichael.Christensen@Sun.COM 		hdr = (ds_hdr_t *)msg;
750*7697SMichael.Christensen@Sun.COM 		hdr->msg_type = DS_INIT_ACK;
751*7697SMichael.Christensen@Sun.COM 		hdr->payload_len = sizeof (ds_init_ack_t);
752*7697SMichael.Christensen@Sun.COM 
753*7697SMichael.Christensen@Sun.COM 		ack = (ds_init_ack_t *)(msg + DS_HDR_SZ);
754*7697SMichael.Christensen@Sun.COM 		ack->minor_vers = MIN(new_minor, req->minor_vers);
755*7697SMichael.Christensen@Sun.COM 
756*7697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_ack>: minor=0x%04X" DS_EOL,
757*7697SMichael.Christensen@Sun.COM 		    PORTID(port), MIN(new_minor, req->minor_vers));
758*7697SMichael.Christensen@Sun.COM 	} else {
759*7697SMichael.Christensen@Sun.COM 		msglen = DS_MSG_LEN(ds_init_nack_t);
760*7697SMichael.Christensen@Sun.COM 		msg = DS_MALLOC(msglen);
761*7697SMichael.Christensen@Sun.COM 
762*7697SMichael.Christensen@Sun.COM 		hdr = (ds_hdr_t *)msg;
763*7697SMichael.Christensen@Sun.COM 		hdr->msg_type = DS_INIT_NACK;
764*7697SMichael.Christensen@Sun.COM 		hdr->payload_len = sizeof (ds_init_nack_t);
765*7697SMichael.Christensen@Sun.COM 
766*7697SMichael.Christensen@Sun.COM 		nack = (ds_init_nack_t *)(msg + DS_HDR_SZ);
767*7697SMichael.Christensen@Sun.COM 		nack->major_vers = new_major;
768*7697SMichael.Christensen@Sun.COM 
769*7697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_nack>: major=0x%04X" DS_EOL,
770*7697SMichael.Christensen@Sun.COM 		    PORTID(port), new_major);
771*7697SMichael.Christensen@Sun.COM 	}
772*7697SMichael.Christensen@Sun.COM 
773*7697SMichael.Christensen@Sun.COM 	/*
774*7697SMichael.Christensen@Sun.COM 	 * Send the response
775*7697SMichael.Christensen@Sun.COM 	 */
776*7697SMichael.Christensen@Sun.COM 	(void) ds_send_msg(port, msg, msglen);
777*7697SMichael.Christensen@Sun.COM 	DS_FREE(msg, msglen);
778*7697SMichael.Christensen@Sun.COM }
779*7697SMichael.Christensen@Sun.COM 
780*7697SMichael.Christensen@Sun.COM static void
781*7697SMichael.Christensen@Sun.COM ds_handle_init_ack(ds_port_t *port, caddr_t buf, size_t len)
782*7697SMichael.Christensen@Sun.COM {
783*7697SMichael.Christensen@Sun.COM 	ds_init_ack_t	*ack;
784*7697SMichael.Christensen@Sun.COM 	ds_ver_t	*ver;
785*7697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_init_ack_t);
786*7697SMichael.Christensen@Sun.COM 
787*7697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
788*7697SMichael.Christensen@Sun.COM 	if (len != explen) {
789*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <init_ack: invalid message "
790*7697SMichael.Christensen@Sun.COM 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
791*7697SMichael.Christensen@Sun.COM 		    explen);
792*7697SMichael.Christensen@Sun.COM 		return;
793*7697SMichael.Christensen@Sun.COM 	}
794*7697SMichael.Christensen@Sun.COM 
795*7697SMichael.Christensen@Sun.COM 	ack = (ds_init_ack_t *)(buf + DS_HDR_SZ);
796*7697SMichael.Christensen@Sun.COM 
797*7697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
798*7697SMichael.Christensen@Sun.COM 
799*7697SMichael.Christensen@Sun.COM 	if (port->state != DS_PORT_INIT_REQ) {
800*7697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_ack: invalid state: %d"
801*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), port->state);
802*7697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
803*7697SMichael.Christensen@Sun.COM 		return;
804*7697SMichael.Christensen@Sun.COM 	}
805*7697SMichael.Christensen@Sun.COM 
806*7697SMichael.Christensen@Sun.COM 	ver = &(ds_vers[port->ver_idx]);
807*7697SMichael.Christensen@Sun.COM 
808*7697SMichael.Christensen@Sun.COM 	/* agreed upon a major version */
809*7697SMichael.Christensen@Sun.COM 	port->ver.major = ver->major;
810*7697SMichael.Christensen@Sun.COM 
811*7697SMichael.Christensen@Sun.COM 	/*
812*7697SMichael.Christensen@Sun.COM 	 * If the returned minor version is larger than
813*7697SMichael.Christensen@Sun.COM 	 * the requested minor version, use the lower of
814*7697SMichael.Christensen@Sun.COM 	 * the two, i.e. the requested version.
815*7697SMichael.Christensen@Sun.COM 	 */
816*7697SMichael.Christensen@Sun.COM 	if (ack->minor_vers >= ver->minor) {
817*7697SMichael.Christensen@Sun.COM 		/*
818*7697SMichael.Christensen@Sun.COM 		 * Use the minor version specified in the
819*7697SMichael.Christensen@Sun.COM 		 * original request.
820*7697SMichael.Christensen@Sun.COM 		 */
821*7697SMichael.Christensen@Sun.COM 		port->ver.minor = ver->minor;
822*7697SMichael.Christensen@Sun.COM 	} else {
823*7697SMichael.Christensen@Sun.COM 		/*
824*7697SMichael.Christensen@Sun.COM 		 * Use the lower minor version returned in
825*7697SMichael.Christensen@Sun.COM 		 * the ack. By definition, all lower minor
826*7697SMichael.Christensen@Sun.COM 		 * versions must be supported.
827*7697SMichael.Christensen@Sun.COM 		 */
828*7697SMichael.Christensen@Sun.COM 		port->ver.minor = ack->minor_vers;
829*7697SMichael.Christensen@Sun.COM 	}
830*7697SMichael.Christensen@Sun.COM 
831*7697SMichael.Christensen@Sun.COM 	port->state = DS_PORT_READY;
832*7697SMichael.Christensen@Sun.COM 
833*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_ack: port ready v%d.%d" DS_EOL,
834*7697SMichael.Christensen@Sun.COM 	    PORTID(port), port->ver.major, port->ver.minor);
835*7697SMichael.Christensen@Sun.COM 
836*7697SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
837*7697SMichael.Christensen@Sun.COM 
838*7697SMichael.Christensen@Sun.COM 	/*
839*7697SMichael.Christensen@Sun.COM 	 * The port came up, so update all the services
840*7697SMichael.Christensen@Sun.COM 	 * with this information. Follow that up with an
841*7697SMichael.Christensen@Sun.COM 	 * attempt to register any service that is not
842*7697SMichael.Christensen@Sun.COM 	 * already registered.
843*7697SMichael.Christensen@Sun.COM 	 */
844*7697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
845*7697SMichael.Christensen@Sun.COM 
846*7697SMichael.Christensen@Sun.COM 	(void) ds_walk_svcs(ds_svc_port_up, port);
847*7697SMichael.Christensen@Sun.COM 	(void) ds_walk_svcs(ds_svc_register, NULL);
848*7697SMichael.Christensen@Sun.COM 
849*7697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
850*7697SMichael.Christensen@Sun.COM }
851*7697SMichael.Christensen@Sun.COM 
852*7697SMichael.Christensen@Sun.COM static void
853*7697SMichael.Christensen@Sun.COM ds_handle_init_nack(ds_port_t *port, caddr_t buf, size_t len)
854*7697SMichael.Christensen@Sun.COM {
855*7697SMichael.Christensen@Sun.COM 	int		idx;
856*7697SMichael.Christensen@Sun.COM 	ds_init_nack_t	*nack;
857*7697SMichael.Christensen@Sun.COM 	ds_ver_t	*ver;
858*7697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_init_nack_t);
859*7697SMichael.Christensen@Sun.COM 
860*7697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
861*7697SMichael.Christensen@Sun.COM 	if (len != explen) {
862*7697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_WARN, "ds@%lx: <init_nack: invalid message "
863*7697SMichael.Christensen@Sun.COM 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
864*7697SMichael.Christensen@Sun.COM 		    explen);
865*7697SMichael.Christensen@Sun.COM 		return;
866*7697SMichael.Christensen@Sun.COM 	}
867*7697SMichael.Christensen@Sun.COM 
868*7697SMichael.Christensen@Sun.COM 	nack = (ds_init_nack_t *)(buf + DS_HDR_SZ);
869*7697SMichael.Christensen@Sun.COM 
870*7697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
871*7697SMichael.Christensen@Sun.COM 
872*7697SMichael.Christensen@Sun.COM 	if (port->state != DS_PORT_INIT_REQ) {
873*7697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_nack: invalid state: %d"
874*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), port->state);
875*7697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
876*7697SMichael.Christensen@Sun.COM 		return;
877*7697SMichael.Christensen@Sun.COM 	}
878*7697SMichael.Christensen@Sun.COM 
879*7697SMichael.Christensen@Sun.COM 	ver = &(ds_vers[port->ver_idx]);
880*7697SMichael.Christensen@Sun.COM 
881*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <init_nack: req=v%d.%d, nack=v%d.x"
882*7697SMichael.Christensen@Sun.COM 	    DS_EOL, PORTID(port), ver->major, ver->minor, nack->major_vers);
883*7697SMichael.Christensen@Sun.COM 
884*7697SMichael.Christensen@Sun.COM 	if (nack->major_vers == 0) {
885*7697SMichael.Christensen@Sun.COM 		/* no supported protocol version */
886*7697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_WARN, "ds@%lx: <init_nack: DS not supported"
887*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port));
888*7697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
889*7697SMichael.Christensen@Sun.COM 		return;
890*7697SMichael.Christensen@Sun.COM 	}
891*7697SMichael.Christensen@Sun.COM 
892*7697SMichael.Christensen@Sun.COM 	/*
893*7697SMichael.Christensen@Sun.COM 	 * Walk the version list, looking for a major version
894*7697SMichael.Christensen@Sun.COM 	 * that is as close to the requested major version as
895*7697SMichael.Christensen@Sun.COM 	 * possible.
896*7697SMichael.Christensen@Sun.COM 	 */
897*7697SMichael.Christensen@Sun.COM 	for (idx = port->ver_idx; idx < DS_NUM_VER; idx++) {
898*7697SMichael.Christensen@Sun.COM 		if (ds_vers[idx].major <= nack->major_vers) {
899*7697SMichael.Christensen@Sun.COM 			/* found a version to try */
900*7697SMichael.Christensen@Sun.COM 			goto done;
901*7697SMichael.Christensen@Sun.COM 		}
902*7697SMichael.Christensen@Sun.COM 	}
903*7697SMichael.Christensen@Sun.COM 
904*7697SMichael.Christensen@Sun.COM 	if (idx == DS_NUM_VER) {
905*7697SMichael.Christensen@Sun.COM 		/* no supported version */
906*7697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_WARN, "ds@%lx: <init_nack: DS v%d.x not "
907*7697SMichael.Christensen@Sun.COM 		    "supported" DS_EOL, PORTID(port), nack->major_vers);
908*7697SMichael.Christensen@Sun.COM 
909*7697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
910*7697SMichael.Christensen@Sun.COM 		return;
911*7697SMichael.Christensen@Sun.COM 	}
912*7697SMichael.Christensen@Sun.COM 
913*7697SMichael.Christensen@Sun.COM done:
914*7697SMichael.Christensen@Sun.COM 	/* start the handshake again */
915*7697SMichael.Christensen@Sun.COM 	port->ver_idx = idx;
916*7697SMichael.Christensen@Sun.COM 	port->state = DS_PORT_LDC_INIT;
917*7697SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
918*7697SMichael.Christensen@Sun.COM 
919*7697SMichael.Christensen@Sun.COM 	ds_send_init_req(port);
920*7697SMichael.Christensen@Sun.COM 
921*7697SMichael.Christensen@Sun.COM }
922*7697SMichael.Christensen@Sun.COM 
923*7697SMichael.Christensen@Sun.COM static ds_svc_t *
924*7697SMichael.Christensen@Sun.COM ds_find_svc_by_id_port(char *svc_id, int is_client, ds_port_t *port)
925*7697SMichael.Christensen@Sun.COM {
926*7697SMichael.Christensen@Sun.COM 	int		idx;
927*7697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc, *found_svc = 0;
928*7697SMichael.Christensen@Sun.COM 	uint32_t	flag_match = is_client ? DSSF_ISCLIENT : 0;
929*7697SMichael.Christensen@Sun.COM 
930*7697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
931*7697SMichael.Christensen@Sun.COM 
932*7697SMichael.Christensen@Sun.COM 	/* walk every table entry */
933*7697SMichael.Christensen@Sun.COM 	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
934*7697SMichael.Christensen@Sun.COM 		svc = ds_svcs.tbl[idx];
935*7697SMichael.Christensen@Sun.COM 		if (DS_SVC_ISFREE(svc))
936*7697SMichael.Christensen@Sun.COM 			continue;
937*7697SMichael.Christensen@Sun.COM 		if (strcmp(svc->cap.svc_id, svc_id) != 0)
938*7697SMichael.Christensen@Sun.COM 			continue;
939*7697SMichael.Christensen@Sun.COM 		if ((svc->flags & DSSF_ISCLIENT) != flag_match)
940*7697SMichael.Christensen@Sun.COM 			continue;
941*7697SMichael.Christensen@Sun.COM 		if (port != NULL && svc->port == port) {
942*7697SMichael.Christensen@Sun.COM 			return (svc);
943*7697SMichael.Christensen@Sun.COM 		} else if (svc->state == DS_SVC_INACTIVE) {
944*7697SMichael.Christensen@Sun.COM 			found_svc = svc;
945*7697SMichael.Christensen@Sun.COM 		} else if (!found_svc) {
946*7697SMichael.Christensen@Sun.COM 			found_svc = svc;
947*7697SMichael.Christensen@Sun.COM 		}
948*7697SMichael.Christensen@Sun.COM 	}
949*7697SMichael.Christensen@Sun.COM 
950*7697SMichael.Christensen@Sun.COM 	return (found_svc);
951*7697SMichael.Christensen@Sun.COM }
952*7697SMichael.Christensen@Sun.COM 
953*7697SMichael.Christensen@Sun.COM static void
954*7697SMichael.Christensen@Sun.COM ds_handle_reg_req(ds_port_t *port, caddr_t buf, size_t len)
955*7697SMichael.Christensen@Sun.COM {
956*7697SMichael.Christensen@Sun.COM 	ds_reg_req_t	*req;
957*7697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
958*7697SMichael.Christensen@Sun.COM 	ds_reg_ack_t	*ack;
959*7697SMichael.Christensen@Sun.COM 	ds_reg_nack_t	*nack;
960*7697SMichael.Christensen@Sun.COM 	char		*msg;
961*7697SMichael.Christensen@Sun.COM 	size_t		msglen;
962*7697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_reg_req_t);
963*7697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc = NULL;
964*7697SMichael.Christensen@Sun.COM 	ds_ver_t	version;
965*7697SMichael.Christensen@Sun.COM 	uint16_t	new_major;
966*7697SMichael.Christensen@Sun.COM 	uint16_t	new_minor;
967*7697SMichael.Christensen@Sun.COM 	boolean_t	match;
968*7697SMichael.Christensen@Sun.COM 
969*7697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
970*7697SMichael.Christensen@Sun.COM 	if (len < explen) {
971*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <reg_req: invalid message "
972*7697SMichael.Christensen@Sun.COM 		    "length (%ld), expected at least %ld" DS_EOL,
973*7697SMichael.Christensen@Sun.COM 		    PORTID(port), len, explen);
974*7697SMichael.Christensen@Sun.COM 		return;
975*7697SMichael.Christensen@Sun.COM 	}
976*7697SMichael.Christensen@Sun.COM 
977*7697SMichael.Christensen@Sun.COM 	req = (ds_reg_req_t *)(buf + DS_HDR_SZ);
978*7697SMichael.Christensen@Sun.COM 
979*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' ver=%d.%d, hdl=0x%llx"
980*7697SMichael.Christensen@Sun.COM 	    DS_EOL, PORTID(port), req->svc_id, req->major_vers, req->minor_vers,
981*7697SMichael.Christensen@Sun.COM 	    (u_longlong_t)req->svc_handle);
982*7697SMichael.Christensen@Sun.COM 
983*7697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
984*7697SMichael.Christensen@Sun.COM 	svc = ds_find_svc_by_id_port(req->svc_id,
985*7697SMichael.Christensen@Sun.COM 	    DS_HDL_ISCLIENT(req->svc_handle) == 0, port);
986*7697SMichael.Christensen@Sun.COM 	if (svc == NULL) {
987*7697SMichael.Christensen@Sun.COM 
988*7697SMichael.Christensen@Sun.COM do_reg_nack:
989*7697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
990*7697SMichael.Christensen@Sun.COM 
991*7697SMichael.Christensen@Sun.COM 		msglen = DS_MSG_LEN(ds_reg_nack_t);
992*7697SMichael.Christensen@Sun.COM 		msg = DS_MALLOC(msglen);
993*7697SMichael.Christensen@Sun.COM 
994*7697SMichael.Christensen@Sun.COM 		hdr = (ds_hdr_t *)msg;
995*7697SMichael.Christensen@Sun.COM 		hdr->msg_type = DS_REG_NACK;
996*7697SMichael.Christensen@Sun.COM 		hdr->payload_len = sizeof (ds_reg_nack_t);
997*7697SMichael.Christensen@Sun.COM 
998*7697SMichael.Christensen@Sun.COM 		nack = (ds_reg_nack_t *)(msg + DS_HDR_SZ);
999*7697SMichael.Christensen@Sun.COM 		nack->svc_handle = req->svc_handle;
1000*7697SMichael.Christensen@Sun.COM 		nack->result = DS_REG_VER_NACK;
1001*7697SMichael.Christensen@Sun.COM 		nack->major_vers = 0;
1002*7697SMichael.Christensen@Sun.COM 
1003*7697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_nack>: '%s'" DS_EOL,
1004*7697SMichael.Christensen@Sun.COM 		    PORTID(port), req->svc_id);
1005*7697SMichael.Christensen@Sun.COM 		/*
1006*7697SMichael.Christensen@Sun.COM 		 * Send the response
1007*7697SMichael.Christensen@Sun.COM 		 */
1008*7697SMichael.Christensen@Sun.COM 		(void) ds_send_msg(port, msg, msglen);
1009*7697SMichael.Christensen@Sun.COM 		DS_FREE(msg, msglen);
1010*7697SMichael.Christensen@Sun.COM 		return;
1011*7697SMichael.Christensen@Sun.COM 	}
1012*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' found, hdl: 0x%llx" DS_EOL,
1013*7697SMichael.Christensen@Sun.COM 	    PORTID(port), req->svc_id, (u_longlong_t)svc->hdl);
1014*7697SMichael.Christensen@Sun.COM 
1015*7697SMichael.Christensen@Sun.COM 	/*
1016*7697SMichael.Christensen@Sun.COM 	 * A client sends out a reg req in order to force service providers to
1017*7697SMichael.Christensen@Sun.COM 	 * initiate a reg req from their end (limitation in the protocol).  If a
1018*7697SMichael.Christensen@Sun.COM 	 * service provider already thinks it's talking to someone on that port,
1019*7697SMichael.Christensen@Sun.COM 	 * something's gone wrong (probably an unreg req request was dropped).
1020*7697SMichael.Christensen@Sun.COM 	 * If we see that the service is in the wrong state, reset it.
1021*7697SMichael.Christensen@Sun.COM 	 */
1022*7697SMichael.Christensen@Sun.COM 
1023*7697SMichael.Christensen@Sun.COM 	if (DS_HDL_ISCLIENT(req->svc_handle)) {
1024*7697SMichael.Christensen@Sun.COM 		if (svc->state != DS_SVC_INACTIVE) {
1025*7697SMichael.Christensen@Sun.COM 			(void) ds_svc_unregister(svc, port);
1026*7697SMichael.Christensen@Sun.COM 		}
1027*7697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' pinging client"
1028*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), req->svc_id);
1029*7697SMichael.Christensen@Sun.COM 		(void) ds_svc_port_up(svc, port);
1030*7697SMichael.Christensen@Sun.COM 		(void) ds_svc_register_onport(svc, port);
1031*7697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
1032*7697SMichael.Christensen@Sun.COM 		return;
1033*7697SMichael.Christensen@Sun.COM 	}
1034*7697SMichael.Christensen@Sun.COM 
1035*7697SMichael.Christensen@Sun.COM 	/*
1036*7697SMichael.Christensen@Sun.COM 	 * Only remote service providers can initiate a registration.  The
1037*7697SMichael.Christensen@Sun.COM 	 * local sevice from here must be a client service.
1038*7697SMichael.Christensen@Sun.COM 	 */
1039*7697SMichael.Christensen@Sun.COM 
1040*7697SMichael.Christensen@Sun.COM 	match = negotiate_version(svc->cap.nvers, svc->cap.vers,
1041*7697SMichael.Christensen@Sun.COM 	    req->major_vers, &new_major, &new_minor);
1042*7697SMichael.Christensen@Sun.COM 
1043*7697SMichael.Christensen@Sun.COM 	/*
1044*7697SMichael.Christensen@Sun.COM 	 * Check version info. ACK only if the major numbers exactly
1045*7697SMichael.Christensen@Sun.COM 	 * match. The service entity can retry with a new minor
1046*7697SMichael.Christensen@Sun.COM 	 * based on the response sent as part of the NACK.
1047*7697SMichael.Christensen@Sun.COM 	 */
1048*7697SMichael.Christensen@Sun.COM 	if (match) {
1049*7697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_req: '%s' svc%d: state: %x "
1050*7697SMichael.Christensen@Sun.COM 		    "svc_portid: %d" DS_EOL, PORTID(port), req->svc_id,
1051*7697SMichael.Christensen@Sun.COM 		    (int)DS_HDL2IDX(svc->hdl), svc->state,
1052*7697SMichael.Christensen@Sun.COM 		    (int)(svc->port == NULL ? -1 : PORTID(svc->port)));
1053*7697SMichael.Christensen@Sun.COM 		/*
1054*7697SMichael.Christensen@Sun.COM 		 * If the current local service is already in use and
1055*7697SMichael.Christensen@Sun.COM 		 * it's not on this port, clone it.
1056*7697SMichael.Christensen@Sun.COM 		 */
1057*7697SMichael.Christensen@Sun.COM 		if (svc->state != DS_SVC_INACTIVE) {
1058*7697SMichael.Christensen@Sun.COM 			if (svc->port != NULL && port == svc->port) {
1059*7697SMichael.Christensen@Sun.COM 				/*
1060*7697SMichael.Christensen@Sun.COM 				 * Someone probably dropped an unreg req
1061*7697SMichael.Christensen@Sun.COM 				 * somewhere.  Force a local unreg.
1062*7697SMichael.Christensen@Sun.COM 				 */
1063*7697SMichael.Christensen@Sun.COM 				(void) ds_svc_unregister(svc, port);
1064*7697SMichael.Christensen@Sun.COM 			} else if (!DS_HDL_ISCLIENT(svc->hdl)) {
1065*7697SMichael.Christensen@Sun.COM 				/*
1066*7697SMichael.Christensen@Sun.COM 				 * Can't clone a non-client (service provider)
1067*7697SMichael.Christensen@Sun.COM 				 * handle.  This is because old in-kernel
1068*7697SMichael.Christensen@Sun.COM 				 * service providers can't deal with multiple
1069*7697SMichael.Christensen@Sun.COM 				 * handles.
1070*7697SMichael.Christensen@Sun.COM 				 */
1071*7697SMichael.Christensen@Sun.COM 				goto do_reg_nack;
1072*7697SMichael.Christensen@Sun.COM 			} else {
1073*7697SMichael.Christensen@Sun.COM 				svc = ds_svc_clone(svc);
1074*7697SMichael.Christensen@Sun.COM 			}
1075*7697SMichael.Christensen@Sun.COM 		}
1076*7697SMichael.Christensen@Sun.COM 		svc->port = port;
1077*7697SMichael.Christensen@Sun.COM 		svc->svc_hdl = req->svc_handle;
1078*7697SMichael.Christensen@Sun.COM 		svc->state = DS_SVC_ACTIVE;
1079*7697SMichael.Christensen@Sun.COM 
1080*7697SMichael.Christensen@Sun.COM 		msglen = DS_MSG_LEN(ds_reg_ack_t);
1081*7697SMichael.Christensen@Sun.COM 		msg = DS_MALLOC(msglen);
1082*7697SMichael.Christensen@Sun.COM 
1083*7697SMichael.Christensen@Sun.COM 		hdr = (ds_hdr_t *)msg;
1084*7697SMichael.Christensen@Sun.COM 		hdr->msg_type = DS_REG_ACK;
1085*7697SMichael.Christensen@Sun.COM 		hdr->payload_len = sizeof (ds_reg_ack_t);
1086*7697SMichael.Christensen@Sun.COM 
1087*7697SMichael.Christensen@Sun.COM 		ack = (ds_reg_ack_t *)(msg + DS_HDR_SZ);
1088*7697SMichael.Christensen@Sun.COM 		ack->svc_handle = req->svc_handle;
1089*7697SMichael.Christensen@Sun.COM 		ack->minor_vers = MIN(new_minor, req->minor_vers);
1090*7697SMichael.Christensen@Sun.COM 
1091*7697SMichael.Christensen@Sun.COM 
1092*7697SMichael.Christensen@Sun.COM 		if (svc->ops.ds_reg_cb) {
1093*7697SMichael.Christensen@Sun.COM 			/* Call the registration callback */
1094*7697SMichael.Christensen@Sun.COM 			version.major = req->major_vers;
1095*7697SMichael.Christensen@Sun.COM 			version.minor = ack->minor_vers;
1096*7697SMichael.Christensen@Sun.COM 			(*svc->ops.ds_reg_cb)(svc->ops.cb_arg, &version,
1097*7697SMichael.Christensen@Sun.COM 			    svc->hdl);
1098*7697SMichael.Christensen@Sun.COM 		}
1099*7697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
1100*7697SMichael.Christensen@Sun.COM 
1101*7697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_ack>: '%s' minor=0x%04X"
1102*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), svc->cap.svc_id,
1103*7697SMichael.Christensen@Sun.COM 		    MIN(new_minor, req->minor_vers));
1104*7697SMichael.Christensen@Sun.COM 	} else {
1105*7697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
1106*7697SMichael.Christensen@Sun.COM 
1107*7697SMichael.Christensen@Sun.COM 		msglen = DS_MSG_LEN(ds_reg_nack_t);
1108*7697SMichael.Christensen@Sun.COM 		msg = DS_MALLOC(msglen);
1109*7697SMichael.Christensen@Sun.COM 
1110*7697SMichael.Christensen@Sun.COM 		hdr = (ds_hdr_t *)msg;
1111*7697SMichael.Christensen@Sun.COM 		hdr->msg_type = DS_REG_NACK;
1112*7697SMichael.Christensen@Sun.COM 		hdr->payload_len = sizeof (ds_reg_nack_t);
1113*7697SMichael.Christensen@Sun.COM 
1114*7697SMichael.Christensen@Sun.COM 		nack = (ds_reg_nack_t *)(msg + DS_HDR_SZ);
1115*7697SMichael.Christensen@Sun.COM 		nack->svc_handle = req->svc_handle;
1116*7697SMichael.Christensen@Sun.COM 		nack->result = DS_REG_VER_NACK;
1117*7697SMichael.Christensen@Sun.COM 		nack->major_vers = new_major;
1118*7697SMichael.Christensen@Sun.COM 
1119*7697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_nack>: '%s' major=0x%04X"
1120*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), svc->cap.svc_id, new_major);
1121*7697SMichael.Christensen@Sun.COM 	}
1122*7697SMichael.Christensen@Sun.COM 
1123*7697SMichael.Christensen@Sun.COM 	/* send message */
1124*7697SMichael.Christensen@Sun.COM 	(void) ds_send_msg(port, msg, msglen);
1125*7697SMichael.Christensen@Sun.COM 	DS_FREE(msg, msglen);
1126*7697SMichael.Christensen@Sun.COM }
1127*7697SMichael.Christensen@Sun.COM 
1128*7697SMichael.Christensen@Sun.COM static void
1129*7697SMichael.Christensen@Sun.COM ds_handle_reg_ack(ds_port_t *port, caddr_t buf, size_t len)
1130*7697SMichael.Christensen@Sun.COM {
1131*7697SMichael.Christensen@Sun.COM 	ds_reg_ack_t	*ack;
1132*7697SMichael.Christensen@Sun.COM 	ds_ver_t	*ver;
1133*7697SMichael.Christensen@Sun.COM 	ds_ver_t	tmpver;
1134*7697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
1135*7697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_reg_ack_t);
1136*7697SMichael.Christensen@Sun.COM 
1137*7697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
1138*7697SMichael.Christensen@Sun.COM 	if (len != explen) {
1139*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <reg_ack: invalid message "
1140*7697SMichael.Christensen@Sun.COM 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
1141*7697SMichael.Christensen@Sun.COM 		    explen);
1142*7697SMichael.Christensen@Sun.COM 		return;
1143*7697SMichael.Christensen@Sun.COM 	}
1144*7697SMichael.Christensen@Sun.COM 
1145*7697SMichael.Christensen@Sun.COM 	ack = (ds_reg_ack_t *)(buf + DS_HDR_SZ);
1146*7697SMichael.Christensen@Sun.COM 
1147*7697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
1148*7697SMichael.Christensen@Sun.COM 
1149*7697SMichael.Christensen@Sun.COM 	/*
1150*7697SMichael.Christensen@Sun.COM 	 * This searches for service based on how we generate handles
1151*7697SMichael.Christensen@Sun.COM 	 * and so only works because this is a reg ack.
1152*7697SMichael.Christensen@Sun.COM 	 */
1153*7697SMichael.Christensen@Sun.COM 	if (DS_HDL_ISCLIENT(ack->svc_handle) ||
1154*7697SMichael.Christensen@Sun.COM 	    (svc = ds_get_svc(ack->svc_handle)) == NULL) {
1155*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <reg_ack: invalid handle 0x%llx"
1156*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), (u_longlong_t)ack->svc_handle);
1157*7697SMichael.Christensen@Sun.COM 		goto done;
1158*7697SMichael.Christensen@Sun.COM 	}
1159*7697SMichael.Christensen@Sun.COM 
1160*7697SMichael.Christensen@Sun.COM 	/* make sure the message makes sense */
1161*7697SMichael.Christensen@Sun.COM 	if (svc->state != DS_SVC_REG_PENDING) {
1162*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <reg_ack: invalid state (%d)" DS_EOL,
1163*7697SMichael.Christensen@Sun.COM 		    PORTID(port), svc->state);
1164*7697SMichael.Christensen@Sun.COM 		goto done;
1165*7697SMichael.Christensen@Sun.COM 	}
1166*7697SMichael.Christensen@Sun.COM 
1167*7697SMichael.Christensen@Sun.COM 	ver = &(svc->cap.vers[svc->ver_idx]);
1168*7697SMichael.Christensen@Sun.COM 
1169*7697SMichael.Christensen@Sun.COM 	/* major version has been agreed upon */
1170*7697SMichael.Christensen@Sun.COM 	svc->ver.major = ver->major;
1171*7697SMichael.Christensen@Sun.COM 
1172*7697SMichael.Christensen@Sun.COM 	if (ack->minor_vers >= ver->minor) {
1173*7697SMichael.Christensen@Sun.COM 		/*
1174*7697SMichael.Christensen@Sun.COM 		 * Use the minor version specified in the
1175*7697SMichael.Christensen@Sun.COM 		 * original request.
1176*7697SMichael.Christensen@Sun.COM 		 */
1177*7697SMichael.Christensen@Sun.COM 		svc->ver.minor = ver->minor;
1178*7697SMichael.Christensen@Sun.COM 	} else {
1179*7697SMichael.Christensen@Sun.COM 		/*
1180*7697SMichael.Christensen@Sun.COM 		 * Use the lower minor version returned in
1181*7697SMichael.Christensen@Sun.COM 		 * the ack. By defninition, all lower minor
1182*7697SMichael.Christensen@Sun.COM 		 * versions must be supported.
1183*7697SMichael.Christensen@Sun.COM 		 */
1184*7697SMichael.Christensen@Sun.COM 		svc->ver.minor = ack->minor_vers;
1185*7697SMichael.Christensen@Sun.COM 	}
1186*7697SMichael.Christensen@Sun.COM 
1187*7697SMichael.Christensen@Sun.COM 	svc->state = DS_SVC_ACTIVE;
1188*7697SMichael.Christensen@Sun.COM 	svc->port = port;
1189*7697SMichael.Christensen@Sun.COM 
1190*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_ack: '%s' v%d.%d ready, hdl=0x%llx"
1191*7697SMichael.Christensen@Sun.COM 	    DS_EOL, PORTID(port), svc->cap.svc_id, svc->ver.major,
1192*7697SMichael.Christensen@Sun.COM 	    svc->ver.minor, (u_longlong_t)svc->hdl);
1193*7697SMichael.Christensen@Sun.COM 
1194*7697SMichael.Christensen@Sun.COM 	/* notify the client that registration is complete */
1195*7697SMichael.Christensen@Sun.COM 	if (svc->ops.ds_reg_cb) {
1196*7697SMichael.Christensen@Sun.COM 		/*
1197*7697SMichael.Christensen@Sun.COM 		 * Use a temporary version structure so that
1198*7697SMichael.Christensen@Sun.COM 		 * the copy in the svc structure cannot be
1199*7697SMichael.Christensen@Sun.COM 		 * modified by the client.
1200*7697SMichael.Christensen@Sun.COM 		 */
1201*7697SMichael.Christensen@Sun.COM 		tmpver.major = svc->ver.major;
1202*7697SMichael.Christensen@Sun.COM 		tmpver.minor = svc->ver.minor;
1203*7697SMichael.Christensen@Sun.COM 
1204*7697SMichael.Christensen@Sun.COM 		(*svc->ops.ds_reg_cb)(svc->ops.cb_arg, &tmpver, svc->hdl);
1205*7697SMichael.Christensen@Sun.COM 	}
1206*7697SMichael.Christensen@Sun.COM 
1207*7697SMichael.Christensen@Sun.COM done:
1208*7697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
1209*7697SMichael.Christensen@Sun.COM }
1210*7697SMichael.Christensen@Sun.COM 
1211*7697SMichael.Christensen@Sun.COM static void
1212*7697SMichael.Christensen@Sun.COM ds_try_next_port(ds_svc_t *svc, int portid)
1213*7697SMichael.Christensen@Sun.COM {
1214*7697SMichael.Christensen@Sun.COM 	ds_port_t *port;
1215*7697SMichael.Christensen@Sun.COM 	ds_portset_t totry;
1216*7697SMichael.Christensen@Sun.COM 	int i;
1217*7697SMichael.Christensen@Sun.COM 
1218*7697SMichael.Christensen@Sun.COM 	DS_DBG_LDC(CE_NOTE, "ds@%x %s" DS_EOL, portid, __func__);
1219*7697SMichael.Christensen@Sun.COM 	DS_PORTSET_NOT(totry, svc->tried);
1220*7697SMichael.Christensen@Sun.COM 	DS_PORTSET_AND(totry, svc->avail);
1221*7697SMichael.Christensen@Sun.COM 	if (DS_PORTSET_ISNULL(totry))
1222*7697SMichael.Christensen@Sun.COM 		return;
1223*7697SMichael.Christensen@Sun.COM 
1224*7697SMichael.Christensen@Sun.COM 	for (i = 0; i < DS_MAX_PORTS; i++, portid++) {
1225*7697SMichael.Christensen@Sun.COM 		if (portid >= DS_MAX_PORTS) {
1226*7697SMichael.Christensen@Sun.COM 			portid = 0;
1227*7697SMichael.Christensen@Sun.COM 		}
1228*7697SMichael.Christensen@Sun.COM 
1229*7697SMichael.Christensen@Sun.COM 		/*
1230*7697SMichael.Christensen@Sun.COM 		 * If the port is not in the available list,
1231*7697SMichael.Christensen@Sun.COM 		 * it is not a candidate for registration.
1232*7697SMichael.Christensen@Sun.COM 		 */
1233*7697SMichael.Christensen@Sun.COM 		if (!DS_PORT_IN_SET(totry, portid)) {
1234*7697SMichael.Christensen@Sun.COM 			continue;
1235*7697SMichael.Christensen@Sun.COM 		}
1236*7697SMichael.Christensen@Sun.COM 
1237*7697SMichael.Christensen@Sun.COM 		port = &ds_ports[portid];
1238*7697SMichael.Christensen@Sun.COM 		DS_DBG_LDC(CE_NOTE, "ds@%x: %s trying ldc.id: %d" DS_EOL,
1239*7697SMichael.Christensen@Sun.COM 		    portid, __func__, (uint_t)(port->ldc.id));
1240*7697SMichael.Christensen@Sun.COM 		if (ds_send_reg_req(svc, port) == 0) {
1241*7697SMichael.Christensen@Sun.COM 			DS_DBG_LDC(CE_NOTE, "ds@%x: %s reg msg send OK" DS_EOL,
1242*7697SMichael.Christensen@Sun.COM 			    portid, __func__);
1243*7697SMichael.Christensen@Sun.COM 			/* register sent successfully */
1244*7697SMichael.Christensen@Sun.COM 			break;
1245*7697SMichael.Christensen@Sun.COM 		}
1246*7697SMichael.Christensen@Sun.COM 		DS_DBG_LDC(CE_NOTE, "ds@%x: %s reg msg send FAIL" DS_EOL,
1247*7697SMichael.Christensen@Sun.COM 		    portid, __func__);
1248*7697SMichael.Christensen@Sun.COM 
1249*7697SMichael.Christensen@Sun.COM 		/* reset the service to try the next port */
1250*7697SMichael.Christensen@Sun.COM 		ds_reset_svc(svc, port);
1251*7697SMichael.Christensen@Sun.COM 	}
1252*7697SMichael.Christensen@Sun.COM }
1253*7697SMichael.Christensen@Sun.COM 
1254*7697SMichael.Christensen@Sun.COM static void
1255*7697SMichael.Christensen@Sun.COM ds_handle_reg_nack(ds_port_t *port, caddr_t buf, size_t len)
1256*7697SMichael.Christensen@Sun.COM {
1257*7697SMichael.Christensen@Sun.COM 	ds_reg_nack_t	*nack;
1258*7697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
1259*7697SMichael.Christensen@Sun.COM 	int		idx;
1260*7697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_reg_nack_t);
1261*7697SMichael.Christensen@Sun.COM 
1262*7697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
1263*7697SMichael.Christensen@Sun.COM 	if (len != explen) {
1264*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <reg_nack: invalid message "
1265*7697SMichael.Christensen@Sun.COM 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
1266*7697SMichael.Christensen@Sun.COM 		    explen);
1267*7697SMichael.Christensen@Sun.COM 		return;
1268*7697SMichael.Christensen@Sun.COM 	}
1269*7697SMichael.Christensen@Sun.COM 
1270*7697SMichael.Christensen@Sun.COM 	nack = (ds_reg_nack_t *)(buf + DS_HDR_SZ);
1271*7697SMichael.Christensen@Sun.COM 
1272*7697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
1273*7697SMichael.Christensen@Sun.COM 
1274*7697SMichael.Christensen@Sun.COM 	/*
1275*7697SMichael.Christensen@Sun.COM 	 * We expect a reg_nack for a client ping.
1276*7697SMichael.Christensen@Sun.COM 	 */
1277*7697SMichael.Christensen@Sun.COM 	if (DS_HDL_ISCLIENT(nack->svc_handle)) {
1278*7697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: ping hdl: 0x%llx"
1279*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle);
1280*7697SMichael.Christensen@Sun.COM 		goto done;
1281*7697SMichael.Christensen@Sun.COM 	}
1282*7697SMichael.Christensen@Sun.COM 
1283*7697SMichael.Christensen@Sun.COM 	/*
1284*7697SMichael.Christensen@Sun.COM 	 * This searches for service based on how we generate handles
1285*7697SMichael.Christensen@Sun.COM 	 * and so only works because this is a reg nack.
1286*7697SMichael.Christensen@Sun.COM 	 */
1287*7697SMichael.Christensen@Sun.COM 	if ((svc = ds_get_svc(nack->svc_handle)) == NULL) {
1288*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <reg_nack: invalid handle 0x%llx"
1289*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle);
1290*7697SMichael.Christensen@Sun.COM 		goto done;
1291*7697SMichael.Christensen@Sun.COM 	}
1292*7697SMichael.Christensen@Sun.COM 
1293*7697SMichael.Christensen@Sun.COM 	/* make sure the message makes sense */
1294*7697SMichael.Christensen@Sun.COM 	if (svc->state != DS_SVC_REG_PENDING) {
1295*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <reg_nack: invalid state (%d)" DS_EOL,
1296*7697SMichael.Christensen@Sun.COM 		    PORTID(port), svc->state);
1297*7697SMichael.Christensen@Sun.COM 		goto done;
1298*7697SMichael.Christensen@Sun.COM 	}
1299*7697SMichael.Christensen@Sun.COM 
1300*7697SMichael.Christensen@Sun.COM 	if (nack->result == DS_REG_DUP) {
1301*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <reg_nack: duplicate registration "
1302*7697SMichael.Christensen@Sun.COM 		    " for %s" DS_EOL, PORTID(port), svc->cap.svc_id);
1303*7697SMichael.Christensen@Sun.COM 		ds_reset_svc(svc, port);
1304*7697SMichael.Christensen@Sun.COM 		goto done;
1305*7697SMichael.Christensen@Sun.COM 	}
1306*7697SMichael.Christensen@Sun.COM 
1307*7697SMichael.Christensen@Sun.COM 	/*
1308*7697SMichael.Christensen@Sun.COM 	 * A major version of zero indicates that the
1309*7697SMichael.Christensen@Sun.COM 	 * service is not supported at all.
1310*7697SMichael.Christensen@Sun.COM 	 */
1311*7697SMichael.Christensen@Sun.COM 	if (nack->major_vers == 0) {
1312*7697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: '%s' not supported"
1313*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), svc->cap.svc_id);
1314*7697SMichael.Christensen@Sun.COM 		ds_reset_svc(svc, port);
1315*7697SMichael.Christensen@Sun.COM 		if ((svc->flags & DSSF_ISCLIENT) == 0)
1316*7697SMichael.Christensen@Sun.COM 			ds_try_next_port(svc, PORTID(port) + 1);
1317*7697SMichael.Christensen@Sun.COM 		goto done;
1318*7697SMichael.Christensen@Sun.COM 	}
1319*7697SMichael.Christensen@Sun.COM 
1320*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: '%s' hdl=0x%llx, nack=%d.x"
1321*7697SMichael.Christensen@Sun.COM 	    DS_EOL, PORTID(port), svc->cap.svc_id,
1322*7697SMichael.Christensen@Sun.COM 	    (u_longlong_t)nack->svc_handle, nack->major_vers);
1323*7697SMichael.Christensen@Sun.COM 
1324*7697SMichael.Christensen@Sun.COM 	/*
1325*7697SMichael.Christensen@Sun.COM 	 * Walk the version list for the service, looking for
1326*7697SMichael.Christensen@Sun.COM 	 * a major version that is as close to the requested
1327*7697SMichael.Christensen@Sun.COM 	 * major version as possible.
1328*7697SMichael.Christensen@Sun.COM 	 */
1329*7697SMichael.Christensen@Sun.COM 	for (idx = svc->ver_idx; idx < svc->cap.nvers; idx++) {
1330*7697SMichael.Christensen@Sun.COM 		if (svc->cap.vers[idx].major <= nack->major_vers) {
1331*7697SMichael.Christensen@Sun.COM 			/* found a version to try */
1332*7697SMichael.Christensen@Sun.COM 			break;
1333*7697SMichael.Christensen@Sun.COM 		}
1334*7697SMichael.Christensen@Sun.COM 	}
1335*7697SMichael.Christensen@Sun.COM 
1336*7697SMichael.Christensen@Sun.COM 	if (idx == svc->cap.nvers) {
1337*7697SMichael.Christensen@Sun.COM 		/* no supported version */
1338*7697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: <reg_nack: %s v%d.x not supported"
1339*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), svc->cap.svc_id, nack->major_vers);
1340*7697SMichael.Christensen@Sun.COM 		ds_reset_svc(svc, port);
1341*7697SMichael.Christensen@Sun.COM 		if ((svc->flags & DSSF_ISCLIENT) == 0)
1342*7697SMichael.Christensen@Sun.COM 			ds_try_next_port(svc, PORTID(port) + 1);
1343*7697SMichael.Christensen@Sun.COM 		goto done;
1344*7697SMichael.Christensen@Sun.COM 	}
1345*7697SMichael.Christensen@Sun.COM 
1346*7697SMichael.Christensen@Sun.COM 	/* start the handshake again */
1347*7697SMichael.Christensen@Sun.COM 	svc->state = DS_SVC_INACTIVE;
1348*7697SMichael.Christensen@Sun.COM 	svc->ver_idx = idx;
1349*7697SMichael.Christensen@Sun.COM 
1350*7697SMichael.Christensen@Sun.COM 	(void) ds_svc_register(svc, NULL);
1351*7697SMichael.Christensen@Sun.COM 
1352*7697SMichael.Christensen@Sun.COM done:
1353*7697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
1354*7697SMichael.Christensen@Sun.COM }
1355*7697SMichael.Christensen@Sun.COM 
1356*7697SMichael.Christensen@Sun.COM static void
1357*7697SMichael.Christensen@Sun.COM ds_handle_unreg_req(ds_port_t *port, caddr_t buf, size_t len)
1358*7697SMichael.Christensen@Sun.COM {
1359*7697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
1360*7697SMichael.Christensen@Sun.COM 	ds_unreg_req_t	*req;
1361*7697SMichael.Christensen@Sun.COM 	ds_unreg_ack_t	*ack;
1362*7697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
1363*7697SMichael.Christensen@Sun.COM 	char		*msg;
1364*7697SMichael.Christensen@Sun.COM 	size_t		msglen;
1365*7697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_unreg_req_t);
1366*7697SMichael.Christensen@Sun.COM 
1367*7697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
1368*7697SMichael.Christensen@Sun.COM 	if (len != explen) {
1369*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <unreg_req: invalid message "
1370*7697SMichael.Christensen@Sun.COM 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
1371*7697SMichael.Christensen@Sun.COM 		    explen);
1372*7697SMichael.Christensen@Sun.COM 		return;
1373*7697SMichael.Christensen@Sun.COM 	}
1374*7697SMichael.Christensen@Sun.COM 
1375*7697SMichael.Christensen@Sun.COM 	req = (ds_unreg_req_t *)(buf + DS_HDR_SZ);
1376*7697SMichael.Christensen@Sun.COM 
1377*7697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
1378*7697SMichael.Christensen@Sun.COM 
1379*7697SMichael.Christensen@Sun.COM 	/* lookup appropriate client or service */
1380*7697SMichael.Christensen@Sun.COM 	if (DS_HDL_ISCLIENT(req->svc_handle) ||
1381*7697SMichael.Christensen@Sun.COM 	    ((svc = ds_find_clnt_svc_by_hdl_port(req->svc_handle, port))
1382*7697SMichael.Christensen@Sun.COM 	    == NULL && ((svc = ds_get_svc(req->svc_handle)) == NULL ||
1383*7697SMichael.Christensen@Sun.COM 	    svc->port != port))) {
1384*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <unreg_req: invalid handle 0x%llx"
1385*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), (u_longlong_t)req->svc_handle);
1386*7697SMichael.Christensen@Sun.COM 		ds_send_unreg_nack(port, req->svc_handle);
1387*7697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
1388*7697SMichael.Christensen@Sun.COM 		return;
1389*7697SMichael.Christensen@Sun.COM 	}
1390*7697SMichael.Christensen@Sun.COM 
1391*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_req: '%s' handle 0x%llx" DS_EOL,
1392*7697SMichael.Christensen@Sun.COM 	    PORTID(port), svc->cap.svc_id, (u_longlong_t)req->svc_handle);
1393*7697SMichael.Christensen@Sun.COM 
1394*7697SMichael.Christensen@Sun.COM 	(void) ds_svc_unregister(svc, svc->port);
1395*7697SMichael.Christensen@Sun.COM 
1396*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: unreg_ack>: '%s' hdl=0x%llx" DS_EOL,
1397*7697SMichael.Christensen@Sun.COM 	    PORTID(port), svc->cap.svc_id, (u_longlong_t)req->svc_handle);
1398*7697SMichael.Christensen@Sun.COM 
1399*7697SMichael.Christensen@Sun.COM 	ds_check_for_dup_services(svc);
1400*7697SMichael.Christensen@Sun.COM 
1401*7697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
1402*7697SMichael.Christensen@Sun.COM 
1403*7697SMichael.Christensen@Sun.COM 	msglen = DS_HDR_SZ + sizeof (ds_unreg_ack_t);
1404*7697SMichael.Christensen@Sun.COM 	msg = DS_MALLOC(msglen);
1405*7697SMichael.Christensen@Sun.COM 
1406*7697SMichael.Christensen@Sun.COM 	hdr = (ds_hdr_t *)msg;
1407*7697SMichael.Christensen@Sun.COM 	hdr->msg_type = DS_UNREG_ACK;
1408*7697SMichael.Christensen@Sun.COM 	hdr->payload_len = sizeof (ds_unreg_ack_t);
1409*7697SMichael.Christensen@Sun.COM 
1410*7697SMichael.Christensen@Sun.COM 	ack = (ds_unreg_ack_t *)(msg + DS_HDR_SZ);
1411*7697SMichael.Christensen@Sun.COM 	ack->svc_handle = req->svc_handle;
1412*7697SMichael.Christensen@Sun.COM 
1413*7697SMichael.Christensen@Sun.COM 	/* send message */
1414*7697SMichael.Christensen@Sun.COM 	(void) ds_send_msg(port, msg, msglen);
1415*7697SMichael.Christensen@Sun.COM 	DS_FREE(msg, msglen);
1416*7697SMichael.Christensen@Sun.COM 
1417*7697SMichael.Christensen@Sun.COM }
1418*7697SMichael.Christensen@Sun.COM 
1419*7697SMichael.Christensen@Sun.COM static void
1420*7697SMichael.Christensen@Sun.COM ds_handle_unreg_ack(ds_port_t *port, caddr_t buf, size_t len)
1421*7697SMichael.Christensen@Sun.COM {
1422*7697SMichael.Christensen@Sun.COM 	ds_unreg_ack_t	*ack;
1423*7697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_unreg_ack_t);
1424*7697SMichael.Christensen@Sun.COM 
1425*7697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
1426*7697SMichael.Christensen@Sun.COM 	if (len != explen) {
1427*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <unreg_ack: invalid message "
1428*7697SMichael.Christensen@Sun.COM 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
1429*7697SMichael.Christensen@Sun.COM 		    explen);
1430*7697SMichael.Christensen@Sun.COM 		return;
1431*7697SMichael.Christensen@Sun.COM 	}
1432*7697SMichael.Christensen@Sun.COM 
1433*7697SMichael.Christensen@Sun.COM 	ack = (ds_unreg_ack_t *)(buf + DS_HDR_SZ);
1434*7697SMichael.Christensen@Sun.COM 
1435*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_ack: hdl=0x%llx" DS_EOL,
1436*7697SMichael.Christensen@Sun.COM 	    PORTID(port), (u_longlong_t)ack->svc_handle);
1437*7697SMichael.Christensen@Sun.COM 
1438*7697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
1439*7697SMichael.Christensen@Sun.COM 
1440*7697SMichael.Christensen@Sun.COM 	/*
1441*7697SMichael.Christensen@Sun.COM 	 * Since the unregister request was initiated locally,
1442*7697SMichael.Christensen@Sun.COM 	 * the service structure has already been torn down.
1443*7697SMichael.Christensen@Sun.COM 	 * Just perform a sanity check to make sure the message
1444*7697SMichael.Christensen@Sun.COM 	 * is appropriate.
1445*7697SMichael.Christensen@Sun.COM 	 */
1446*7697SMichael.Christensen@Sun.COM 	if (ds_get_svc(ack->svc_handle) != NULL) {
1447*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <unreg_ack: handle 0x%llx in use"
1448*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), (u_longlong_t)ack->svc_handle);
1449*7697SMichael.Christensen@Sun.COM 	}
1450*7697SMichael.Christensen@Sun.COM 
1451*7697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
1452*7697SMichael.Christensen@Sun.COM }
1453*7697SMichael.Christensen@Sun.COM 
1454*7697SMichael.Christensen@Sun.COM static void
1455*7697SMichael.Christensen@Sun.COM ds_handle_unreg_nack(ds_port_t *port, caddr_t buf, size_t len)
1456*7697SMichael.Christensen@Sun.COM {
1457*7697SMichael.Christensen@Sun.COM 	ds_unreg_nack_t	*nack;
1458*7697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_unreg_nack_t);
1459*7697SMichael.Christensen@Sun.COM 
1460*7697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
1461*7697SMichael.Christensen@Sun.COM 	if (len != explen) {
1462*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <unreg_nack: invalid message "
1463*7697SMichael.Christensen@Sun.COM 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
1464*7697SMichael.Christensen@Sun.COM 		    explen);
1465*7697SMichael.Christensen@Sun.COM 		return;
1466*7697SMichael.Christensen@Sun.COM 	}
1467*7697SMichael.Christensen@Sun.COM 
1468*7697SMichael.Christensen@Sun.COM 	nack = (ds_unreg_nack_t *)(buf + DS_HDR_SZ);
1469*7697SMichael.Christensen@Sun.COM 
1470*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <unreg_nack: hdl=0x%llx" DS_EOL,
1471*7697SMichael.Christensen@Sun.COM 	    PORTID(port), (u_longlong_t)nack->svc_handle);
1472*7697SMichael.Christensen@Sun.COM 
1473*7697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
1474*7697SMichael.Christensen@Sun.COM 
1475*7697SMichael.Christensen@Sun.COM 	/*
1476*7697SMichael.Christensen@Sun.COM 	 * Since the unregister request was initiated locally,
1477*7697SMichael.Christensen@Sun.COM 	 * the service structure has already been torn down.
1478*7697SMichael.Christensen@Sun.COM 	 * Just perform a sanity check to make sure the message
1479*7697SMichael.Christensen@Sun.COM 	 * is appropriate.
1480*7697SMichael.Christensen@Sun.COM 	 */
1481*7697SMichael.Christensen@Sun.COM 	if (ds_get_svc(nack->svc_handle) != NULL) {
1482*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <unreg_nack: handle 0x%llx in use"
1483*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle);
1484*7697SMichael.Christensen@Sun.COM 	}
1485*7697SMichael.Christensen@Sun.COM 
1486*7697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
1487*7697SMichael.Christensen@Sun.COM }
1488*7697SMichael.Christensen@Sun.COM 
1489*7697SMichael.Christensen@Sun.COM static void
1490*7697SMichael.Christensen@Sun.COM ds_handle_data(ds_port_t *port, caddr_t buf, size_t len)
1491*7697SMichael.Christensen@Sun.COM {
1492*7697SMichael.Christensen@Sun.COM 	ds_data_handle_t	*data;
1493*7697SMichael.Christensen@Sun.COM 	ds_svc_t		*svc;
1494*7697SMichael.Christensen@Sun.COM 	char			*msg;
1495*7697SMichael.Christensen@Sun.COM 	int			msgsz;
1496*7697SMichael.Christensen@Sun.COM 	int			hdrsz;
1497*7697SMichael.Christensen@Sun.COM 	size_t			explen = DS_MSG_LEN(ds_data_handle_t);
1498*7697SMichael.Christensen@Sun.COM 
1499*7697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
1500*7697SMichael.Christensen@Sun.COM 	if (len < explen) {
1501*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <data: invalid message length "
1502*7697SMichael.Christensen@Sun.COM 		    "(%ld), expected at least %ld" DS_EOL, PORTID(port), len,
1503*7697SMichael.Christensen@Sun.COM 		    explen);
1504*7697SMichael.Christensen@Sun.COM 		return;
1505*7697SMichael.Christensen@Sun.COM 	}
1506*7697SMichael.Christensen@Sun.COM 
1507*7697SMichael.Christensen@Sun.COM 	data = (ds_data_handle_t *)(buf + DS_HDR_SZ);
1508*7697SMichael.Christensen@Sun.COM 
1509*7697SMichael.Christensen@Sun.COM 	hdrsz = DS_HDR_SZ + sizeof (ds_data_handle_t);
1510*7697SMichael.Christensen@Sun.COM 	msgsz = len - hdrsz;
1511*7697SMichael.Christensen@Sun.COM 
1512*7697SMichael.Christensen@Sun.COM 	/* strip off the header for the client */
1513*7697SMichael.Christensen@Sun.COM 	msg = (msgsz) ? (buf + hdrsz) : NULL;
1514*7697SMichael.Christensen@Sun.COM 
1515*7697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
1516*7697SMichael.Christensen@Sun.COM 
1517*7697SMichael.Christensen@Sun.COM 	if ((svc = ds_find_clnt_svc_by_hdl_port(data->svc_handle, port))
1518*7697SMichael.Christensen@Sun.COM 	    == NULL) {
1519*7697SMichael.Christensen@Sun.COM 		if ((svc = ds_get_svc(data->svc_handle)) == NULL) {
1520*7697SMichael.Christensen@Sun.COM 			mutex_exit(&ds_svcs.lock);
1521*7697SMichael.Christensen@Sun.COM 			cmn_err(CE_WARN, "ds@%lx: <data: invalid handle 0x%llx"
1522*7697SMichael.Christensen@Sun.COM 			    DS_EOL, PORTID(port),
1523*7697SMichael.Christensen@Sun.COM 			    (u_longlong_t)data->svc_handle);
1524*7697SMichael.Christensen@Sun.COM 			ds_send_data_nack(port, data->svc_handle);
1525*7697SMichael.Christensen@Sun.COM 			return;
1526*7697SMichael.Christensen@Sun.COM 		}
1527*7697SMichael.Christensen@Sun.COM 	}
1528*7697SMichael.Christensen@Sun.COM 
1529*7697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
1530*7697SMichael.Christensen@Sun.COM 
1531*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: <data: '%s' hdl=0x%llx" DS_EOL,
1532*7697SMichael.Christensen@Sun.COM 	    PORTID(port), svc->cap.svc_id, (u_longlong_t)svc->hdl);
1533*7697SMichael.Christensen@Sun.COM 	DS_DUMP_MSG(DS_DBG_FLAG_PRCL, msg, msgsz);
1534*7697SMichael.Christensen@Sun.COM 
1535*7697SMichael.Christensen@Sun.COM 	/* dispatch this message to the client */
1536*7697SMichael.Christensen@Sun.COM 	(*svc->ops.ds_data_cb)(svc->ops.cb_arg, msg, msgsz);
1537*7697SMichael.Christensen@Sun.COM }
1538*7697SMichael.Christensen@Sun.COM 
1539*7697SMichael.Christensen@Sun.COM static void
1540*7697SMichael.Christensen@Sun.COM ds_handle_nack(ds_port_t *port, caddr_t buf, size_t len)
1541*7697SMichael.Christensen@Sun.COM {
1542*7697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
1543*7697SMichael.Christensen@Sun.COM 	ds_data_nack_t	*nack;
1544*7697SMichael.Christensen@Sun.COM 	size_t		explen = DS_MSG_LEN(ds_data_nack_t);
1545*7697SMichael.Christensen@Sun.COM 
1546*7697SMichael.Christensen@Sun.COM 	/* sanity check the incoming message */
1547*7697SMichael.Christensen@Sun.COM 	if (len != explen) {
1548*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <data_nack: invalid message "
1549*7697SMichael.Christensen@Sun.COM 		    "length (%ld), expected %ld" DS_EOL, PORTID(port), len,
1550*7697SMichael.Christensen@Sun.COM 		    explen);
1551*7697SMichael.Christensen@Sun.COM 		return;
1552*7697SMichael.Christensen@Sun.COM 	}
1553*7697SMichael.Christensen@Sun.COM 
1554*7697SMichael.Christensen@Sun.COM 	nack = (ds_data_nack_t *)(buf + DS_HDR_SZ);
1555*7697SMichael.Christensen@Sun.COM 
1556*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: data_nack: hdl=0x%llx, result=0x%llx"
1557*7697SMichael.Christensen@Sun.COM 	    DS_EOL, PORTID(port), (u_longlong_t)nack->svc_handle,
1558*7697SMichael.Christensen@Sun.COM 	    (u_longlong_t)nack->result);
1559*7697SMichael.Christensen@Sun.COM 
1560*7697SMichael.Christensen@Sun.COM 	if (nack->result == DS_INV_HDL) {
1561*7697SMichael.Christensen@Sun.COM 
1562*7697SMichael.Christensen@Sun.COM 		mutex_enter(&ds_svcs.lock);
1563*7697SMichael.Christensen@Sun.COM 
1564*7697SMichael.Christensen@Sun.COM 		if ((svc = ds_find_clnt_svc_by_hdl_port(nack->svc_handle,
1565*7697SMichael.Christensen@Sun.COM 		    port)) == NULL) {
1566*7697SMichael.Christensen@Sun.COM 			if ((svc = ds_get_svc(nack->svc_handle)) == NULL) {
1567*7697SMichael.Christensen@Sun.COM 				mutex_exit(&ds_svcs.lock);
1568*7697SMichael.Christensen@Sun.COM 				return;
1569*7697SMichael.Christensen@Sun.COM 			}
1570*7697SMichael.Christensen@Sun.COM 		}
1571*7697SMichael.Christensen@Sun.COM 
1572*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: <data_nack: handle 0x%llx reported "
1573*7697SMichael.Christensen@Sun.COM 		    " as invalid" DS_EOL, PORTID(port),
1574*7697SMichael.Christensen@Sun.COM 		    (u_longlong_t)nack->svc_handle);
1575*7697SMichael.Christensen@Sun.COM 
1576*7697SMichael.Christensen@Sun.COM 		(void) ds_svc_unregister(svc, svc->port);
1577*7697SMichael.Christensen@Sun.COM 
1578*7697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
1579*7697SMichael.Christensen@Sun.COM 	}
1580*7697SMichael.Christensen@Sun.COM }
1581*7697SMichael.Christensen@Sun.COM 
1582*7697SMichael.Christensen@Sun.COM /* Initialize the port */
1583*7697SMichael.Christensen@Sun.COM void
1584*7697SMichael.Christensen@Sun.COM ds_send_init_req(ds_port_t *port)
1585*7697SMichael.Christensen@Sun.COM {
1586*7697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
1587*7697SMichael.Christensen@Sun.COM 	ds_init_req_t	*init_req;
1588*7697SMichael.Christensen@Sun.COM 	size_t		msglen;
1589*7697SMichael.Christensen@Sun.COM 	ds_ver_t	*vers = &ds_vers[port->ver_idx];
1590*7697SMichael.Christensen@Sun.COM 
1591*7697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
1592*7697SMichael.Christensen@Sun.COM 	if (port->state != DS_PORT_LDC_INIT) {
1593*7697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_req>: invalid state: %d"
1594*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), port->state);
1595*7697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
1596*7697SMichael.Christensen@Sun.COM 		return;
1597*7697SMichael.Christensen@Sun.COM 	}
1598*7697SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
1599*7697SMichael.Christensen@Sun.COM 
1600*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: init_req>: req=v%d.%d" DS_EOL,
1601*7697SMichael.Christensen@Sun.COM 	    PORTID(port), vers->major, vers->minor);
1602*7697SMichael.Christensen@Sun.COM 
1603*7697SMichael.Christensen@Sun.COM 	msglen = DS_HDR_SZ + sizeof (ds_init_req_t);
1604*7697SMichael.Christensen@Sun.COM 	hdr = DS_MALLOC(msglen);
1605*7697SMichael.Christensen@Sun.COM 
1606*7697SMichael.Christensen@Sun.COM 	hdr->msg_type = DS_INIT_REQ;
1607*7697SMichael.Christensen@Sun.COM 	hdr->payload_len = sizeof (ds_init_req_t);
1608*7697SMichael.Christensen@Sun.COM 
1609*7697SMichael.Christensen@Sun.COM 	init_req = (ds_init_req_t *)((caddr_t)hdr + DS_HDR_SZ);
1610*7697SMichael.Christensen@Sun.COM 	init_req->major_vers = vers->major;
1611*7697SMichael.Christensen@Sun.COM 	init_req->minor_vers = vers->minor;
1612*7697SMichael.Christensen@Sun.COM 
1613*7697SMichael.Christensen@Sun.COM 	if (ds_send_msg(port, (caddr_t)hdr, msglen) == 0) {
1614*7697SMichael.Christensen@Sun.COM 		/*
1615*7697SMichael.Christensen@Sun.COM 		 * We've left the port state unlocked over the malloc/send,
1616*7697SMichael.Christensen@Sun.COM 		 * make sure no one has changed the state under us before
1617*7697SMichael.Christensen@Sun.COM 		 * we update the state.
1618*7697SMichael.Christensen@Sun.COM 		 */
1619*7697SMichael.Christensen@Sun.COM 		mutex_enter(&port->lock);
1620*7697SMichael.Christensen@Sun.COM 		if (port->state == DS_PORT_LDC_INIT)
1621*7697SMichael.Christensen@Sun.COM 			port->state = DS_PORT_INIT_REQ;
1622*7697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
1623*7697SMichael.Christensen@Sun.COM 	}
1624*7697SMichael.Christensen@Sun.COM 	DS_FREE(hdr, msglen);
1625*7697SMichael.Christensen@Sun.COM }
1626*7697SMichael.Christensen@Sun.COM 
1627*7697SMichael.Christensen@Sun.COM static int
1628*7697SMichael.Christensen@Sun.COM ds_send_reg_req(ds_svc_t *svc, ds_port_t *port)
1629*7697SMichael.Christensen@Sun.COM {
1630*7697SMichael.Christensen@Sun.COM 	ds_ver_t	*ver;
1631*7697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
1632*7697SMichael.Christensen@Sun.COM 	caddr_t		msg;
1633*7697SMichael.Christensen@Sun.COM 	size_t		msglen;
1634*7697SMichael.Christensen@Sun.COM 	ds_reg_req_t	*req;
1635*7697SMichael.Christensen@Sun.COM 	size_t		idlen;
1636*7697SMichael.Christensen@Sun.COM 	int		rv;
1637*7697SMichael.Christensen@Sun.COM 
1638*7697SMichael.Christensen@Sun.COM 	ASSERT(svc->state == DS_SVC_INACTIVE ||
1639*7697SMichael.Christensen@Sun.COM 	    (svc->flags & DSSF_ISCLIENT) != 0);
1640*7697SMichael.Christensen@Sun.COM 
1641*7697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
1642*7697SMichael.Christensen@Sun.COM 
1643*7697SMichael.Christensen@Sun.COM 	/* check on the LDC to Zeus */
1644*7697SMichael.Christensen@Sun.COM 	if (port->ldc.state != LDC_UP) {
1645*7697SMichael.Christensen@Sun.COM 		/* can not send message */
1646*7697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_req>: channel %ld is not up"
1647*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), port->ldc.id);
1648*7697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
1649*7697SMichael.Christensen@Sun.COM 		return (-1);
1650*7697SMichael.Christensen@Sun.COM 	}
1651*7697SMichael.Christensen@Sun.COM 
1652*7697SMichael.Christensen@Sun.COM 	/* make sure port is ready */
1653*7697SMichael.Christensen@Sun.COM 	if (port->state != DS_PORT_READY) {
1654*7697SMichael.Christensen@Sun.COM 		/* can not send message */
1655*7697SMichael.Christensen@Sun.COM 		DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_req>: port is not ready"
1656*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port));
1657*7697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
1658*7697SMichael.Christensen@Sun.COM 		return (-1);
1659*7697SMichael.Christensen@Sun.COM 	}
1660*7697SMichael.Christensen@Sun.COM 
1661*7697SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
1662*7697SMichael.Christensen@Sun.COM 
1663*7697SMichael.Christensen@Sun.COM 	/* allocate the message buffer */
1664*7697SMichael.Christensen@Sun.COM 	idlen = strlen(svc->cap.svc_id);
1665*7697SMichael.Christensen@Sun.COM 	msglen = DS_HDR_SZ + sizeof (ds_reg_req_t) + idlen;
1666*7697SMichael.Christensen@Sun.COM 	msg = DS_MALLOC(msglen);
1667*7697SMichael.Christensen@Sun.COM 
1668*7697SMichael.Christensen@Sun.COM 	/* copy in the header data */
1669*7697SMichael.Christensen@Sun.COM 	hdr = (ds_hdr_t *)msg;
1670*7697SMichael.Christensen@Sun.COM 	hdr->msg_type = DS_REG_REQ;
1671*7697SMichael.Christensen@Sun.COM 	hdr->payload_len = sizeof (ds_reg_req_t) + idlen;
1672*7697SMichael.Christensen@Sun.COM 
1673*7697SMichael.Christensen@Sun.COM 	req = (ds_reg_req_t *)(msg + DS_HDR_SZ);
1674*7697SMichael.Christensen@Sun.COM 	req->svc_handle = svc->hdl;
1675*7697SMichael.Christensen@Sun.COM 	ver = &(svc->cap.vers[svc->ver_idx]);
1676*7697SMichael.Christensen@Sun.COM 	req->major_vers = ver->major;
1677*7697SMichael.Christensen@Sun.COM 	req->minor_vers = ver->minor;
1678*7697SMichael.Christensen@Sun.COM 
1679*7697SMichael.Christensen@Sun.COM 	/* copy in the service id */
1680*7697SMichael.Christensen@Sun.COM 	(void) memcpy(req->svc_id, svc->cap.svc_id, idlen + 1);
1681*7697SMichael.Christensen@Sun.COM 
1682*7697SMichael.Christensen@Sun.COM 	/* send the message */
1683*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: reg_req>: '%s' ver=%d.%d, hdl=0x%llx"
1684*7697SMichael.Christensen@Sun.COM 	    DS_EOL, PORTID(port), svc->cap.svc_id, ver->major, ver->minor,
1685*7697SMichael.Christensen@Sun.COM 	    (u_longlong_t)svc->hdl);
1686*7697SMichael.Christensen@Sun.COM 
1687*7697SMichael.Christensen@Sun.COM 	if ((rv = ds_send_msg(port, msg, msglen)) != 0) {
1688*7697SMichael.Christensen@Sun.COM 		svc->port = port;
1689*7697SMichael.Christensen@Sun.COM 		rv = -1;
1690*7697SMichael.Christensen@Sun.COM 	} else if ((svc->flags & DSSF_ISCLIENT) == 0) {
1691*7697SMichael.Christensen@Sun.COM 		svc->state = DS_SVC_REG_PENDING;
1692*7697SMichael.Christensen@Sun.COM 	}
1693*7697SMichael.Christensen@Sun.COM 	DS_FREE(msg, msglen);
1694*7697SMichael.Christensen@Sun.COM 
1695*7697SMichael.Christensen@Sun.COM 	return (rv);
1696*7697SMichael.Christensen@Sun.COM }
1697*7697SMichael.Christensen@Sun.COM 
1698*7697SMichael.Christensen@Sun.COM /*
1699*7697SMichael.Christensen@Sun.COM  * Keep around in case we want this later
1700*7697SMichael.Christensen@Sun.COM  */
1701*7697SMichael.Christensen@Sun.COM int
1702*7697SMichael.Christensen@Sun.COM ds_send_unreg_req(ds_svc_t *svc)
1703*7697SMichael.Christensen@Sun.COM {
1704*7697SMichael.Christensen@Sun.COM 	caddr_t		msg;
1705*7697SMichael.Christensen@Sun.COM 	size_t		msglen;
1706*7697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
1707*7697SMichael.Christensen@Sun.COM 	ds_unreg_req_t	*req;
1708*7697SMichael.Christensen@Sun.COM 	ds_port_t	*port = svc->port;
1709*7697SMichael.Christensen@Sun.COM 	int		rv;
1710*7697SMichael.Christensen@Sun.COM 
1711*7697SMichael.Christensen@Sun.COM 	if (port == NULL) {
1712*7697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "send_unreg_req: service '%s' not "
1713*7697SMichael.Christensen@Sun.COM 		    "associated with a port" DS_EOL, svc->cap.svc_id);
1714*7697SMichael.Christensen@Sun.COM 		return (-1);
1715*7697SMichael.Christensen@Sun.COM 	}
1716*7697SMichael.Christensen@Sun.COM 
1717*7697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
1718*7697SMichael.Christensen@Sun.COM 
1719*7697SMichael.Christensen@Sun.COM 	/* check on the LDC to Zeus */
1720*7697SMichael.Christensen@Sun.COM 	if (port->ldc.state != LDC_UP) {
1721*7697SMichael.Christensen@Sun.COM 		/* can not send message */
1722*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: unreg_req>: channel %ld is not up"
1723*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), port->ldc.id);
1724*7697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
1725*7697SMichael.Christensen@Sun.COM 		return (-1);
1726*7697SMichael.Christensen@Sun.COM 	}
1727*7697SMichael.Christensen@Sun.COM 
1728*7697SMichael.Christensen@Sun.COM 	/* make sure port is ready */
1729*7697SMichael.Christensen@Sun.COM 	if (port->state != DS_PORT_READY) {
1730*7697SMichael.Christensen@Sun.COM 		/* can not send message */
1731*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: unreg_req>: port is not ready" DS_EOL,
1732*7697SMichael.Christensen@Sun.COM 		    PORTID(port));
1733*7697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
1734*7697SMichael.Christensen@Sun.COM 		return (-1);
1735*7697SMichael.Christensen@Sun.COM 	}
1736*7697SMichael.Christensen@Sun.COM 
1737*7697SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
1738*7697SMichael.Christensen@Sun.COM 
1739*7697SMichael.Christensen@Sun.COM 	msglen = DS_HDR_SZ + sizeof (ds_unreg_req_t);
1740*7697SMichael.Christensen@Sun.COM 	msg = DS_MALLOC(msglen);
1741*7697SMichael.Christensen@Sun.COM 
1742*7697SMichael.Christensen@Sun.COM 	/* copy in the header data */
1743*7697SMichael.Christensen@Sun.COM 	hdr = (ds_hdr_t *)msg;
1744*7697SMichael.Christensen@Sun.COM 	hdr->msg_type = DS_UNREG;
1745*7697SMichael.Christensen@Sun.COM 	hdr->payload_len = sizeof (ds_unreg_req_t);
1746*7697SMichael.Christensen@Sun.COM 
1747*7697SMichael.Christensen@Sun.COM 	req = (ds_unreg_req_t *)(msg + DS_HDR_SZ);
1748*7697SMichael.Christensen@Sun.COM 	if (svc->flags & DSSF_ISCLIENT) {
1749*7697SMichael.Christensen@Sun.COM 		req->svc_handle = svc->svc_hdl;
1750*7697SMichael.Christensen@Sun.COM 	} else {
1751*7697SMichael.Christensen@Sun.COM 		req->svc_handle = svc->hdl;
1752*7697SMichael.Christensen@Sun.COM 	}
1753*7697SMichael.Christensen@Sun.COM 
1754*7697SMichael.Christensen@Sun.COM 	/* send the message */
1755*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: unreg_req>: '%s' hdl=0x%llx" DS_EOL,
1756*7697SMichael.Christensen@Sun.COM 	    PORTID(port), (svc->cap.svc_id) ? svc->cap.svc_id : "NULL",
1757*7697SMichael.Christensen@Sun.COM 	    (u_longlong_t)svc->hdl);
1758*7697SMichael.Christensen@Sun.COM 
1759*7697SMichael.Christensen@Sun.COM 	if ((rv = ds_send_msg(port, msg, msglen)) != 0) {
1760*7697SMichael.Christensen@Sun.COM 		rv = -1;
1761*7697SMichael.Christensen@Sun.COM 	}
1762*7697SMichael.Christensen@Sun.COM 	DS_FREE(msg, msglen);
1763*7697SMichael.Christensen@Sun.COM 
1764*7697SMichael.Christensen@Sun.COM 	return (rv);
1765*7697SMichael.Christensen@Sun.COM }
1766*7697SMichael.Christensen@Sun.COM 
1767*7697SMichael.Christensen@Sun.COM static void
1768*7697SMichael.Christensen@Sun.COM ds_send_unreg_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl)
1769*7697SMichael.Christensen@Sun.COM {
1770*7697SMichael.Christensen@Sun.COM 	caddr_t		msg;
1771*7697SMichael.Christensen@Sun.COM 	size_t		msglen;
1772*7697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
1773*7697SMichael.Christensen@Sun.COM 	ds_unreg_nack_t	*nack;
1774*7697SMichael.Christensen@Sun.COM 
1775*7697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
1776*7697SMichael.Christensen@Sun.COM 
1777*7697SMichael.Christensen@Sun.COM 	/* check on the LDC to Zeus */
1778*7697SMichael.Christensen@Sun.COM 	if (port->ldc.state != LDC_UP) {
1779*7697SMichael.Christensen@Sun.COM 		/* can not send message */
1780*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: unreg_nack>: channel %ld is not up"
1781*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), port->ldc.id);
1782*7697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
1783*7697SMichael.Christensen@Sun.COM 		return;
1784*7697SMichael.Christensen@Sun.COM 	}
1785*7697SMichael.Christensen@Sun.COM 
1786*7697SMichael.Christensen@Sun.COM 	/* make sure port is ready */
1787*7697SMichael.Christensen@Sun.COM 	if (port->state != DS_PORT_READY) {
1788*7697SMichael.Christensen@Sun.COM 		/* can not send message */
1789*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: unreg_nack>: port is not ready"
1790*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port));
1791*7697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
1792*7697SMichael.Christensen@Sun.COM 		return;
1793*7697SMichael.Christensen@Sun.COM 	}
1794*7697SMichael.Christensen@Sun.COM 
1795*7697SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
1796*7697SMichael.Christensen@Sun.COM 
1797*7697SMichael.Christensen@Sun.COM 	msglen = DS_HDR_SZ + sizeof (ds_unreg_nack_t);
1798*7697SMichael.Christensen@Sun.COM 	msg = DS_MALLOC(msglen);
1799*7697SMichael.Christensen@Sun.COM 
1800*7697SMichael.Christensen@Sun.COM 	/* copy in the header data */
1801*7697SMichael.Christensen@Sun.COM 	hdr = (ds_hdr_t *)msg;
1802*7697SMichael.Christensen@Sun.COM 	hdr->msg_type = DS_UNREG_NACK;
1803*7697SMichael.Christensen@Sun.COM 	hdr->payload_len = sizeof (ds_unreg_nack_t);
1804*7697SMichael.Christensen@Sun.COM 
1805*7697SMichael.Christensen@Sun.COM 	nack = (ds_unreg_nack_t *)(msg + DS_HDR_SZ);
1806*7697SMichael.Christensen@Sun.COM 	nack->svc_handle = bad_hdl;
1807*7697SMichael.Christensen@Sun.COM 
1808*7697SMichael.Christensen@Sun.COM 	/* send the message */
1809*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: unreg_nack>: hdl=0x%llx" DS_EOL,
1810*7697SMichael.Christensen@Sun.COM 	    PORTID(port), (u_longlong_t)bad_hdl);
1811*7697SMichael.Christensen@Sun.COM 
1812*7697SMichael.Christensen@Sun.COM 	(void) ds_send_msg(port, msg, msglen);
1813*7697SMichael.Christensen@Sun.COM 	DS_FREE(msg, msglen);
1814*7697SMichael.Christensen@Sun.COM }
1815*7697SMichael.Christensen@Sun.COM 
1816*7697SMichael.Christensen@Sun.COM static void
1817*7697SMichael.Christensen@Sun.COM ds_send_data_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl)
1818*7697SMichael.Christensen@Sun.COM {
1819*7697SMichael.Christensen@Sun.COM 	caddr_t		msg;
1820*7697SMichael.Christensen@Sun.COM 	size_t		msglen;
1821*7697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
1822*7697SMichael.Christensen@Sun.COM 	ds_data_nack_t	*nack;
1823*7697SMichael.Christensen@Sun.COM 
1824*7697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
1825*7697SMichael.Christensen@Sun.COM 
1826*7697SMichael.Christensen@Sun.COM 	/* check on the LDC to Zeus */
1827*7697SMichael.Christensen@Sun.COM 	if (port->ldc.state != LDC_UP) {
1828*7697SMichael.Christensen@Sun.COM 		/* can not send message */
1829*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: data_nack>: channel %ld is not up"
1830*7697SMichael.Christensen@Sun.COM 		    DS_EOL, PORTID(port), port->ldc.id);
1831*7697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
1832*7697SMichael.Christensen@Sun.COM 		return;
1833*7697SMichael.Christensen@Sun.COM 	}
1834*7697SMichael.Christensen@Sun.COM 
1835*7697SMichael.Christensen@Sun.COM 	/* make sure port is ready */
1836*7697SMichael.Christensen@Sun.COM 	if (port->state != DS_PORT_READY) {
1837*7697SMichael.Christensen@Sun.COM 		/* can not send message */
1838*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "ds@%lx: data_nack>: port is not ready" DS_EOL,
1839*7697SMichael.Christensen@Sun.COM 		    PORTID(port));
1840*7697SMichael.Christensen@Sun.COM 		mutex_exit(&port->lock);
1841*7697SMichael.Christensen@Sun.COM 		return;
1842*7697SMichael.Christensen@Sun.COM 	}
1843*7697SMichael.Christensen@Sun.COM 
1844*7697SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
1845*7697SMichael.Christensen@Sun.COM 
1846*7697SMichael.Christensen@Sun.COM 	msglen = DS_HDR_SZ + sizeof (ds_data_nack_t);
1847*7697SMichael.Christensen@Sun.COM 	msg = DS_MALLOC(msglen);
1848*7697SMichael.Christensen@Sun.COM 
1849*7697SMichael.Christensen@Sun.COM 	/* copy in the header data */
1850*7697SMichael.Christensen@Sun.COM 	hdr = (ds_hdr_t *)msg;
1851*7697SMichael.Christensen@Sun.COM 	hdr->msg_type = DS_NACK;
1852*7697SMichael.Christensen@Sun.COM 	hdr->payload_len = sizeof (ds_data_nack_t);
1853*7697SMichael.Christensen@Sun.COM 
1854*7697SMichael.Christensen@Sun.COM 	nack = (ds_data_nack_t *)(msg + DS_HDR_SZ);
1855*7697SMichael.Christensen@Sun.COM 	nack->svc_handle = bad_hdl;
1856*7697SMichael.Christensen@Sun.COM 	nack->result = DS_INV_HDL;
1857*7697SMichael.Christensen@Sun.COM 
1858*7697SMichael.Christensen@Sun.COM 	/* send the message */
1859*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: data_nack>: hdl=0x%llx" DS_EOL,
1860*7697SMichael.Christensen@Sun.COM 	    PORTID(port), (u_longlong_t)bad_hdl);
1861*7697SMichael.Christensen@Sun.COM 
1862*7697SMichael.Christensen@Sun.COM 	(void) ds_send_msg(port, msg, msglen);
1863*7697SMichael.Christensen@Sun.COM 	DS_FREE(msg, msglen);
1864*7697SMichael.Christensen@Sun.COM }
1865*7697SMichael.Christensen@Sun.COM 
1866*7697SMichael.Christensen@Sun.COM /* END DS PROTOCOL SUPPORT FUNCTIONS */
1867*7697SMichael.Christensen@Sun.COM 
1868*7697SMichael.Christensen@Sun.COM #ifdef DEBUG
1869*7697SMichael.Christensen@Sun.COM 
1870*7697SMichael.Christensen@Sun.COM #define	BYTESPERLINE	8
1871*7697SMichael.Christensen@Sun.COM #define	LINEWIDTH	((BYTESPERLINE * 3) + (BYTESPERLINE + 2) + 1)
1872*7697SMichael.Christensen@Sun.COM #define	ASCIIOFFSET	((BYTESPERLINE * 3) + 2)
1873*7697SMichael.Christensen@Sun.COM #define	ISPRINT(c)	((c >= ' ') && (c <= '~'))
1874*7697SMichael.Christensen@Sun.COM 
1875*7697SMichael.Christensen@Sun.COM /*
1876*7697SMichael.Christensen@Sun.COM  * Output a buffer formatted with a set number of bytes on
1877*7697SMichael.Christensen@Sun.COM  * each line. Append each line with the ASCII equivalent of
1878*7697SMichael.Christensen@Sun.COM  * each byte if it falls within the printable ASCII range,
1879*7697SMichael.Christensen@Sun.COM  * and '.' otherwise.
1880*7697SMichael.Christensen@Sun.COM  */
1881*7697SMichael.Christensen@Sun.COM void
1882*7697SMichael.Christensen@Sun.COM ds_dump_msg(void *vbuf, size_t len)
1883*7697SMichael.Christensen@Sun.COM {
1884*7697SMichael.Christensen@Sun.COM 	int	i, j;
1885*7697SMichael.Christensen@Sun.COM 	char	*curr;
1886*7697SMichael.Christensen@Sun.COM 	char	*aoff;
1887*7697SMichael.Christensen@Sun.COM 	char	line[LINEWIDTH];
1888*7697SMichael.Christensen@Sun.COM 	uint8_t	*buf = vbuf;
1889*7697SMichael.Christensen@Sun.COM 
1890*7697SMichael.Christensen@Sun.COM 	if (len > 128)
1891*7697SMichael.Christensen@Sun.COM 		len = 128;
1892*7697SMichael.Christensen@Sun.COM 
1893*7697SMichael.Christensen@Sun.COM 	/* walk the buffer one line at a time */
1894*7697SMichael.Christensen@Sun.COM 	for (i = 0; i < len; i += BYTESPERLINE) {
1895*7697SMichael.Christensen@Sun.COM 
1896*7697SMichael.Christensen@Sun.COM 		bzero(line, LINEWIDTH);
1897*7697SMichael.Christensen@Sun.COM 
1898*7697SMichael.Christensen@Sun.COM 		curr = line;
1899*7697SMichael.Christensen@Sun.COM 		aoff = line + ASCIIOFFSET;
1900*7697SMichael.Christensen@Sun.COM 
1901*7697SMichael.Christensen@Sun.COM 		/*
1902*7697SMichael.Christensen@Sun.COM 		 * Walk the bytes in the current line, storing
1903*7697SMichael.Christensen@Sun.COM 		 * the hex value for the byte as well as the
1904*7697SMichael.Christensen@Sun.COM 		 * ASCII representation in a temporary buffer.
1905*7697SMichael.Christensen@Sun.COM 		 * All ASCII values are placed at the end of
1906*7697SMichael.Christensen@Sun.COM 		 * the line.
1907*7697SMichael.Christensen@Sun.COM 		 */
1908*7697SMichael.Christensen@Sun.COM 		for (j = 0; (j < BYTESPERLINE) && ((i + j) < len); j++) {
1909*7697SMichael.Christensen@Sun.COM 			(void) sprintf(curr, " %02x", buf[i + j]);
1910*7697SMichael.Christensen@Sun.COM 			*aoff = (ISPRINT(buf[i + j])) ? buf[i + j] : '.';
1911*7697SMichael.Christensen@Sun.COM 			curr += 3;
1912*7697SMichael.Christensen@Sun.COM 			aoff++;
1913*7697SMichael.Christensen@Sun.COM 		}
1914*7697SMichael.Christensen@Sun.COM 
1915*7697SMichael.Christensen@Sun.COM 		/*
1916*7697SMichael.Christensen@Sun.COM 		 * Fill in to the start of the ASCII translation
1917*7697SMichael.Christensen@Sun.COM 		 * with spaces. This will only be necessary if
1918*7697SMichael.Christensen@Sun.COM 		 * this is the last line and there are not enough
1919*7697SMichael.Christensen@Sun.COM 		 * bytes to fill the whole line.
1920*7697SMichael.Christensen@Sun.COM 		 */
1921*7697SMichael.Christensen@Sun.COM 		while (curr != (line + ASCIIOFFSET))
1922*7697SMichael.Christensen@Sun.COM 			*curr++ = ' ';
1923*7697SMichael.Christensen@Sun.COM 
1924*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_NOTE, "%s" DS_EOL, line);
1925*7697SMichael.Christensen@Sun.COM 	}
1926*7697SMichael.Christensen@Sun.COM }
1927*7697SMichael.Christensen@Sun.COM #endif /* DEBUG */
1928*7697SMichael.Christensen@Sun.COM 
1929*7697SMichael.Christensen@Sun.COM 
1930*7697SMichael.Christensen@Sun.COM /*
1931*7697SMichael.Christensen@Sun.COM  * Walk the table of registered services, executing the specified callback
1932*7697SMichael.Christensen@Sun.COM  * function for each service on a port. A non-zero return value from the
1933*7697SMichael.Christensen@Sun.COM  * callback is used to terminate the walk, not to indicate an error. Returns
1934*7697SMichael.Christensen@Sun.COM  * the index of the last service visited.
1935*7697SMichael.Christensen@Sun.COM  */
1936*7697SMichael.Christensen@Sun.COM int
1937*7697SMichael.Christensen@Sun.COM ds_walk_svcs(svc_cb_t svc_cb, void *arg)
1938*7697SMichael.Christensen@Sun.COM {
1939*7697SMichael.Christensen@Sun.COM 	int		idx;
1940*7697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
1941*7697SMichael.Christensen@Sun.COM 
1942*7697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
1943*7697SMichael.Christensen@Sun.COM 
1944*7697SMichael.Christensen@Sun.COM 	/* walk every table entry */
1945*7697SMichael.Christensen@Sun.COM 	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
1946*7697SMichael.Christensen@Sun.COM 		svc = ds_svcs.tbl[idx];
1947*7697SMichael.Christensen@Sun.COM 
1948*7697SMichael.Christensen@Sun.COM 		/* execute the callback */
1949*7697SMichael.Christensen@Sun.COM 		if ((*svc_cb)(svc, arg) != 0)
1950*7697SMichael.Christensen@Sun.COM 			break;
1951*7697SMichael.Christensen@Sun.COM 	}
1952*7697SMichael.Christensen@Sun.COM 
1953*7697SMichael.Christensen@Sun.COM 	return (idx);
1954*7697SMichael.Christensen@Sun.COM }
1955*7697SMichael.Christensen@Sun.COM 
1956*7697SMichael.Christensen@Sun.COM static int
1957*7697SMichael.Christensen@Sun.COM ds_svc_isfree(ds_svc_t *svc, void *arg)
1958*7697SMichael.Christensen@Sun.COM {
1959*7697SMichael.Christensen@Sun.COM 	_NOTE(ARGUNUSED(arg))
1960*7697SMichael.Christensen@Sun.COM 
1961*7697SMichael.Christensen@Sun.COM 	/*
1962*7697SMichael.Christensen@Sun.COM 	 * Looking for a free service. This may be a NULL entry
1963*7697SMichael.Christensen@Sun.COM 	 * in the table, or an unused structure that could be
1964*7697SMichael.Christensen@Sun.COM 	 * reused.
1965*7697SMichael.Christensen@Sun.COM 	 */
1966*7697SMichael.Christensen@Sun.COM 
1967*7697SMichael.Christensen@Sun.COM 	if (DS_SVC_ISFREE(svc)) {
1968*7697SMichael.Christensen@Sun.COM 		/* yes, it is free */
1969*7697SMichael.Christensen@Sun.COM 		return (1);
1970*7697SMichael.Christensen@Sun.COM 	}
1971*7697SMichael.Christensen@Sun.COM 
1972*7697SMichael.Christensen@Sun.COM 	/* not a candidate */
1973*7697SMichael.Christensen@Sun.COM 	return (0);
1974*7697SMichael.Christensen@Sun.COM }
1975*7697SMichael.Christensen@Sun.COM 
1976*7697SMichael.Christensen@Sun.COM int
1977*7697SMichael.Christensen@Sun.COM ds_svc_ismatch(ds_svc_t *svc, void *arg)
1978*7697SMichael.Christensen@Sun.COM {
1979*7697SMichael.Christensen@Sun.COM 	if (DS_SVC_ISFREE(svc)) {
1980*7697SMichael.Christensen@Sun.COM 		return (0);
1981*7697SMichael.Christensen@Sun.COM 	}
1982*7697SMichael.Christensen@Sun.COM 
1983*7697SMichael.Christensen@Sun.COM 	if (strcmp(svc->cap.svc_id, arg) == 0 &&
1984*7697SMichael.Christensen@Sun.COM 	    (svc->flags & DSSF_ISCLIENT) == 0) {
1985*7697SMichael.Christensen@Sun.COM 		/* found a match */
1986*7697SMichael.Christensen@Sun.COM 		return (1);
1987*7697SMichael.Christensen@Sun.COM 	}
1988*7697SMichael.Christensen@Sun.COM 
1989*7697SMichael.Christensen@Sun.COM 	return (0);
1990*7697SMichael.Christensen@Sun.COM }
1991*7697SMichael.Christensen@Sun.COM 
1992*7697SMichael.Christensen@Sun.COM int
1993*7697SMichael.Christensen@Sun.COM ds_svc_clnt_ismatch(ds_svc_t *svc, void *arg)
1994*7697SMichael.Christensen@Sun.COM {
1995*7697SMichael.Christensen@Sun.COM 	if (DS_SVC_ISFREE(svc)) {
1996*7697SMichael.Christensen@Sun.COM 		return (0);
1997*7697SMichael.Christensen@Sun.COM 	}
1998*7697SMichael.Christensen@Sun.COM 
1999*7697SMichael.Christensen@Sun.COM 	if (strcmp(svc->cap.svc_id, arg) == 0 &&
2000*7697SMichael.Christensen@Sun.COM 	    (svc->flags & DSSF_ISCLIENT) != 0) {
2001*7697SMichael.Christensen@Sun.COM 		/* found a match */
2002*7697SMichael.Christensen@Sun.COM 		return (1);
2003*7697SMichael.Christensen@Sun.COM 	}
2004*7697SMichael.Christensen@Sun.COM 
2005*7697SMichael.Christensen@Sun.COM 	return (0);
2006*7697SMichael.Christensen@Sun.COM }
2007*7697SMichael.Christensen@Sun.COM 
2008*7697SMichael.Christensen@Sun.COM int
2009*7697SMichael.Christensen@Sun.COM ds_svc_free(ds_svc_t *svc, void *arg)
2010*7697SMichael.Christensen@Sun.COM {
2011*7697SMichael.Christensen@Sun.COM 	_NOTE(ARGUNUSED(arg))
2012*7697SMichael.Christensen@Sun.COM 
2013*7697SMichael.Christensen@Sun.COM 	if (svc == NULL) {
2014*7697SMichael.Christensen@Sun.COM 		return (0);
2015*7697SMichael.Christensen@Sun.COM 	}
2016*7697SMichael.Christensen@Sun.COM 
2017*7697SMichael.Christensen@Sun.COM 	if (svc->cap.svc_id) {
2018*7697SMichael.Christensen@Sun.COM 		DS_FREE(svc->cap.svc_id, strlen(svc->cap.svc_id) + 1);
2019*7697SMichael.Christensen@Sun.COM 		svc->cap.svc_id = NULL;
2020*7697SMichael.Christensen@Sun.COM 	}
2021*7697SMichael.Christensen@Sun.COM 
2022*7697SMichael.Christensen@Sun.COM 	if (svc->cap.vers) {
2023*7697SMichael.Christensen@Sun.COM 		DS_FREE(svc->cap.vers, svc->cap.nvers * sizeof (ds_ver_t));
2024*7697SMichael.Christensen@Sun.COM 		svc->cap.vers = NULL;
2025*7697SMichael.Christensen@Sun.COM 	}
2026*7697SMichael.Christensen@Sun.COM 
2027*7697SMichael.Christensen@Sun.COM 	DS_FREE(svc, sizeof (ds_svc_t));
2028*7697SMichael.Christensen@Sun.COM 
2029*7697SMichael.Christensen@Sun.COM 	return (0);
2030*7697SMichael.Christensen@Sun.COM }
2031*7697SMichael.Christensen@Sun.COM 
2032*7697SMichael.Christensen@Sun.COM static int
2033*7697SMichael.Christensen@Sun.COM ds_svc_register_onport(ds_svc_t *svc, ds_port_t *port)
2034*7697SMichael.Christensen@Sun.COM {
2035*7697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2036*7697SMichael.Christensen@Sun.COM 
2037*7697SMichael.Christensen@Sun.COM 	if (DS_SVC_ISFREE(svc))
2038*7697SMichael.Christensen@Sun.COM 		return (0);
2039*7697SMichael.Christensen@Sun.COM 
2040*7697SMichael.Christensen@Sun.COM 	if (!DS_PORT_IN_SET(svc->avail, PORTID(port)))
2041*7697SMichael.Christensen@Sun.COM 		return (0);
2042*7697SMichael.Christensen@Sun.COM 
2043*7697SMichael.Christensen@Sun.COM 	DS_PORTSET_ADD(svc->tried, PORTID(port));
2044*7697SMichael.Christensen@Sun.COM 
2045*7697SMichael.Christensen@Sun.COM 	if (ds_send_reg_req(svc, port) == 0) {
2046*7697SMichael.Christensen@Sun.COM 		/* register sent successfully */
2047*7697SMichael.Christensen@Sun.COM 		return (1);
2048*7697SMichael.Christensen@Sun.COM 	}
2049*7697SMichael.Christensen@Sun.COM 
2050*7697SMichael.Christensen@Sun.COM 	if ((svc->flags & DSSF_ISCLIENT) == 0) {
2051*7697SMichael.Christensen@Sun.COM 		/* reset the service */
2052*7697SMichael.Christensen@Sun.COM 		ds_reset_svc(svc, port);
2053*7697SMichael.Christensen@Sun.COM 	}
2054*7697SMichael.Christensen@Sun.COM 	return (0);
2055*7697SMichael.Christensen@Sun.COM }
2056*7697SMichael.Christensen@Sun.COM 
2057*7697SMichael.Christensen@Sun.COM int
2058*7697SMichael.Christensen@Sun.COM ds_svc_register(ds_svc_t *svc, void *arg)
2059*7697SMichael.Christensen@Sun.COM {
2060*7697SMichael.Christensen@Sun.COM 	_NOTE(ARGUNUSED(arg))
2061*7697SMichael.Christensen@Sun.COM 	ds_portset_t ports;
2062*7697SMichael.Christensen@Sun.COM 	ds_port_t *port;
2063*7697SMichael.Christensen@Sun.COM 	int	idx;
2064*7697SMichael.Christensen@Sun.COM 
2065*7697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2066*7697SMichael.Christensen@Sun.COM 
2067*7697SMichael.Christensen@Sun.COM 	if (DS_SVC_ISFREE(svc))
2068*7697SMichael.Christensen@Sun.COM 		return (0);
2069*7697SMichael.Christensen@Sun.COM 
2070*7697SMichael.Christensen@Sun.COM 	ports = svc->avail;
2071*7697SMichael.Christensen@Sun.COM 	if (svc->flags & DSSF_ISCLIENT) {
2072*7697SMichael.Christensen@Sun.COM 		ds_portset_del_active_clients(svc->cap.svc_id, &ports);
2073*7697SMichael.Christensen@Sun.COM 	} else if (svc->state != DS_SVC_INACTIVE)
2074*7697SMichael.Christensen@Sun.COM 		return (0);
2075*7697SMichael.Christensen@Sun.COM 
2076*7697SMichael.Christensen@Sun.COM 	if (DS_PORTSET_ISNULL(ports))
2077*7697SMichael.Christensen@Sun.COM 		return (0);
2078*7697SMichael.Christensen@Sun.COM 
2079*7697SMichael.Christensen@Sun.COM 	/*
2080*7697SMichael.Christensen@Sun.COM 	 * Attempt to register the service. Start with the lowest
2081*7697SMichael.Christensen@Sun.COM 	 * numbered port and continue until a registration message
2082*7697SMichael.Christensen@Sun.COM 	 * is sent successfully, or there are no ports left to try.
2083*7697SMichael.Christensen@Sun.COM 	 */
2084*7697SMichael.Christensen@Sun.COM 	for (idx = 0; idx < DS_MAX_PORTS; idx++) {
2085*7697SMichael.Christensen@Sun.COM 
2086*7697SMichael.Christensen@Sun.COM 		/*
2087*7697SMichael.Christensen@Sun.COM 		 * If the port is not in the available list,
2088*7697SMichael.Christensen@Sun.COM 		 * it is not a candidate for registration.
2089*7697SMichael.Christensen@Sun.COM 		 */
2090*7697SMichael.Christensen@Sun.COM 		if (!DS_PORT_IN_SET(ports, idx)) {
2091*7697SMichael.Christensen@Sun.COM 			continue;
2092*7697SMichael.Christensen@Sun.COM 		}
2093*7697SMichael.Christensen@Sun.COM 
2094*7697SMichael.Christensen@Sun.COM 		port = &ds_ports[idx];
2095*7697SMichael.Christensen@Sun.COM 		if (ds_svc_register_onport(svc, port)) {
2096*7697SMichael.Christensen@Sun.COM 			if ((svc->flags & DSSF_ISCLIENT) == 0)
2097*7697SMichael.Christensen@Sun.COM 				break;
2098*7697SMichael.Christensen@Sun.COM 			DS_PORTSET_DEL(svc->avail, idx);
2099*7697SMichael.Christensen@Sun.COM 		}
2100*7697SMichael.Christensen@Sun.COM 	}
2101*7697SMichael.Christensen@Sun.COM 
2102*7697SMichael.Christensen@Sun.COM 	return (0);
2103*7697SMichael.Christensen@Sun.COM }
2104*7697SMichael.Christensen@Sun.COM 
2105*7697SMichael.Christensen@Sun.COM static int
2106*7697SMichael.Christensen@Sun.COM ds_svc_unregister(ds_svc_t *svc, void *arg)
2107*7697SMichael.Christensen@Sun.COM {
2108*7697SMichael.Christensen@Sun.COM 	ds_port_t *port = (ds_port_t *)arg;
2109*7697SMichael.Christensen@Sun.COM 	ds_svc_hdl_t hdl;
2110*7697SMichael.Christensen@Sun.COM 
2111*7697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2112*7697SMichael.Christensen@Sun.COM 
2113*7697SMichael.Christensen@Sun.COM 	if (DS_SVC_ISFREE(svc)) {
2114*7697SMichael.Christensen@Sun.COM 		return (0);
2115*7697SMichael.Christensen@Sun.COM 	}
2116*7697SMichael.Christensen@Sun.COM 
2117*7697SMichael.Christensen@Sun.COM 	/* make sure the service is using this port */
2118*7697SMichael.Christensen@Sun.COM 	if (svc->port != port) {
2119*7697SMichael.Christensen@Sun.COM 		return (0);
2120*7697SMichael.Christensen@Sun.COM 	}
2121*7697SMichael.Christensen@Sun.COM 
2122*7697SMichael.Christensen@Sun.COM 	if (port) {
2123*7697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "ds@%lx: svc_unreg: id='%s', ver=%d.%d, "
2124*7697SMichael.Christensen@Sun.COM 		    " hdl=0x%09lx" DS_EOL, PORTID(port), svc->cap.svc_id,
2125*7697SMichael.Christensen@Sun.COM 		    svc->ver.major, svc->ver.minor, svc->hdl);
2126*7697SMichael.Christensen@Sun.COM 	} else {
2127*7697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "port=NULL: svc_unreg: id='%s', ver=%d.%d, "
2128*7697SMichael.Christensen@Sun.COM 		    " hdl=0x%09lx" DS_EOL, svc->cap.svc_id, svc->ver.major,
2129*7697SMichael.Christensen@Sun.COM 		    svc->ver.minor, svc->hdl);
2130*7697SMichael.Christensen@Sun.COM 	}
2131*7697SMichael.Christensen@Sun.COM 
2132*7697SMichael.Christensen@Sun.COM 	/* reset the service structure */
2133*7697SMichael.Christensen@Sun.COM 	ds_reset_svc(svc, port);
2134*7697SMichael.Christensen@Sun.COM 
2135*7697SMichael.Christensen@Sun.COM 	/* call the client unregister callback */
2136*7697SMichael.Christensen@Sun.COM 	if (svc->ops.ds_unreg_cb) {
2137*7697SMichael.Christensen@Sun.COM 		(*svc->ops.ds_unreg_cb)(svc->ops.cb_arg);
2138*7697SMichael.Christensen@Sun.COM 	}
2139*7697SMichael.Christensen@Sun.COM 
2140*7697SMichael.Christensen@Sun.COM 	/* increment the count in the handle to prevent reuse */
2141*7697SMichael.Christensen@Sun.COM 	hdl = DS_ALLOC_HDL(DS_HDL2IDX(svc->hdl), DS_HDL2COUNT(svc->hdl));
2142*7697SMichael.Christensen@Sun.COM 	if (DS_HDL_ISCLIENT(svc->hdl)) {
2143*7697SMichael.Christensen@Sun.COM 		DS_HDL_SET_ISCLIENT(hdl);
2144*7697SMichael.Christensen@Sun.COM 	}
2145*7697SMichael.Christensen@Sun.COM 	svc->hdl = hdl;
2146*7697SMichael.Christensen@Sun.COM 
2147*7697SMichael.Christensen@Sun.COM 	if (svc->state != DS_SVC_UNREG_PENDING) {
2148*7697SMichael.Christensen@Sun.COM 		/* try to initiate a new registration */
2149*7697SMichael.Christensen@Sun.COM 		(void) ds_svc_register(svc, NULL);
2150*7697SMichael.Christensen@Sun.COM 	}
2151*7697SMichael.Christensen@Sun.COM 
2152*7697SMichael.Christensen@Sun.COM 	return (0);
2153*7697SMichael.Christensen@Sun.COM }
2154*7697SMichael.Christensen@Sun.COM 
2155*7697SMichael.Christensen@Sun.COM static int
2156*7697SMichael.Christensen@Sun.COM ds_svc_port_up(ds_svc_t *svc, void *arg)
2157*7697SMichael.Christensen@Sun.COM {
2158*7697SMichael.Christensen@Sun.COM 	ds_port_t *port = (ds_port_t *)arg;
2159*7697SMichael.Christensen@Sun.COM 
2160*7697SMichael.Christensen@Sun.COM 	if (DS_SVC_ISFREE(svc)) {
2161*7697SMichael.Christensen@Sun.COM 		/* nothing to do */
2162*7697SMichael.Christensen@Sun.COM 		return (0);
2163*7697SMichael.Christensen@Sun.COM 	}
2164*7697SMichael.Christensen@Sun.COM 
2165*7697SMichael.Christensen@Sun.COM 	DS_PORTSET_ADD(svc->avail, port->id);
2166*7697SMichael.Christensen@Sun.COM 	DS_PORTSET_DEL(svc->tried, port->id);
2167*7697SMichael.Christensen@Sun.COM 
2168*7697SMichael.Christensen@Sun.COM 	return (0);
2169*7697SMichael.Christensen@Sun.COM }
2170*7697SMichael.Christensen@Sun.COM 
2171*7697SMichael.Christensen@Sun.COM ds_svc_t *
2172*7697SMichael.Christensen@Sun.COM ds_alloc_svc(void)
2173*7697SMichael.Christensen@Sun.COM {
2174*7697SMichael.Christensen@Sun.COM 	int		idx;
2175*7697SMichael.Christensen@Sun.COM 	uint_t		newmaxsvcs;
2176*7697SMichael.Christensen@Sun.COM 	ds_svc_t	**newtbl;
2177*7697SMichael.Christensen@Sun.COM 	ds_svc_t	*newsvc;
2178*7697SMichael.Christensen@Sun.COM 
2179*7697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2180*7697SMichael.Christensen@Sun.COM 
2181*7697SMichael.Christensen@Sun.COM 	idx = ds_walk_svcs(ds_svc_isfree, NULL);
2182*7697SMichael.Christensen@Sun.COM 
2183*7697SMichael.Christensen@Sun.COM 	if (idx != ds_svcs.maxsvcs) {
2184*7697SMichael.Christensen@Sun.COM 		goto found;
2185*7697SMichael.Christensen@Sun.COM 	}
2186*7697SMichael.Christensen@Sun.COM 
2187*7697SMichael.Christensen@Sun.COM 	/*
2188*7697SMichael.Christensen@Sun.COM 	 * There was no free space in the table. Grow
2189*7697SMichael.Christensen@Sun.COM 	 * the table to double its current size.
2190*7697SMichael.Christensen@Sun.COM 	 */
2191*7697SMichael.Christensen@Sun.COM 	newmaxsvcs = ds_svcs.maxsvcs * 2;
2192*7697SMichael.Christensen@Sun.COM 	newtbl = DS_MALLOC(newmaxsvcs * sizeof (ds_svc_t *));
2193*7697SMichael.Christensen@Sun.COM 
2194*7697SMichael.Christensen@Sun.COM 	/* copy old table data to the new table */
2195*7697SMichael.Christensen@Sun.COM 	(void) memcpy(newtbl, ds_svcs.tbl,
2196*7697SMichael.Christensen@Sun.COM 	    ds_svcs.maxsvcs * sizeof (ds_svc_t *));
2197*7697SMichael.Christensen@Sun.COM 
2198*7697SMichael.Christensen@Sun.COM 	/* clean up the old table */
2199*7697SMichael.Christensen@Sun.COM 	DS_FREE(ds_svcs.tbl, ds_svcs.maxsvcs * sizeof (ds_svc_t *));
2200*7697SMichael.Christensen@Sun.COM 	ds_svcs.tbl = newtbl;
2201*7697SMichael.Christensen@Sun.COM 	ds_svcs.maxsvcs = newmaxsvcs;
2202*7697SMichael.Christensen@Sun.COM 
2203*7697SMichael.Christensen@Sun.COM 	/* search for a free space again */
2204*7697SMichael.Christensen@Sun.COM 	idx = ds_walk_svcs(ds_svc_isfree, NULL);
2205*7697SMichael.Christensen@Sun.COM 
2206*7697SMichael.Christensen@Sun.COM 	/* the table is locked so should find a free slot */
2207*7697SMichael.Christensen@Sun.COM 	ASSERT(idx != ds_svcs.maxsvcs);
2208*7697SMichael.Christensen@Sun.COM 
2209*7697SMichael.Christensen@Sun.COM found:
2210*7697SMichael.Christensen@Sun.COM 	/* allocate a new svc structure if necessary */
2211*7697SMichael.Christensen@Sun.COM 	if ((newsvc = ds_svcs.tbl[idx]) == NULL) {
2212*7697SMichael.Christensen@Sun.COM 		/* allocate a new service */
2213*7697SMichael.Christensen@Sun.COM 		newsvc = DS_MALLOC(sizeof (ds_svc_t));
2214*7697SMichael.Christensen@Sun.COM 		ds_svcs.tbl[idx] = newsvc;
2215*7697SMichael.Christensen@Sun.COM 	}
2216*7697SMichael.Christensen@Sun.COM 
2217*7697SMichael.Christensen@Sun.COM 	/* fill in the handle */
2218*7697SMichael.Christensen@Sun.COM 	newsvc->hdl = DS_ALLOC_HDL(idx, DS_HDL2COUNT(newsvc->hdl));
2219*7697SMichael.Christensen@Sun.COM 	newsvc->state = DS_SVC_FREE;	/* Mark as free temporarily */
2220*7697SMichael.Christensen@Sun.COM 
2221*7697SMichael.Christensen@Sun.COM 	return (newsvc);
2222*7697SMichael.Christensen@Sun.COM }
2223*7697SMichael.Christensen@Sun.COM 
2224*7697SMichael.Christensen@Sun.COM static void
2225*7697SMichael.Christensen@Sun.COM ds_reset_svc(ds_svc_t *svc, ds_port_t *port)
2226*7697SMichael.Christensen@Sun.COM {
2227*7697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2228*7697SMichael.Christensen@Sun.COM 
2229*7697SMichael.Christensen@Sun.COM 	if (svc->state != DS_SVC_UNREG_PENDING)
2230*7697SMichael.Christensen@Sun.COM 		svc->state = DS_SVC_INACTIVE;
2231*7697SMichael.Christensen@Sun.COM 	svc->ver_idx = 0;
2232*7697SMichael.Christensen@Sun.COM 	svc->ver.major = 0;
2233*7697SMichael.Christensen@Sun.COM 	svc->ver.minor = 0;
2234*7697SMichael.Christensen@Sun.COM 	svc->port = NULL;
2235*7697SMichael.Christensen@Sun.COM 	if (port) {
2236*7697SMichael.Christensen@Sun.COM 		DS_PORTSET_DEL(svc->avail, port->id);
2237*7697SMichael.Christensen@Sun.COM 	}
2238*7697SMichael.Christensen@Sun.COM }
2239*7697SMichael.Christensen@Sun.COM 
2240*7697SMichael.Christensen@Sun.COM ds_svc_t *
2241*7697SMichael.Christensen@Sun.COM ds_get_svc(ds_svc_hdl_t hdl)
2242*7697SMichael.Christensen@Sun.COM {
2243*7697SMichael.Christensen@Sun.COM 	int		idx;
2244*7697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
2245*7697SMichael.Christensen@Sun.COM 
2246*7697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2247*7697SMichael.Christensen@Sun.COM 
2248*7697SMichael.Christensen@Sun.COM 	if (hdl == DS_INVALID_HDL)
2249*7697SMichael.Christensen@Sun.COM 		return (NULL);
2250*7697SMichael.Christensen@Sun.COM 
2251*7697SMichael.Christensen@Sun.COM 	idx = DS_HDL2IDX(hdl);
2252*7697SMichael.Christensen@Sun.COM 
2253*7697SMichael.Christensen@Sun.COM 	/* check if index is out of bounds */
2254*7697SMichael.Christensen@Sun.COM 	if ((idx < 0) || (idx >= ds_svcs.maxsvcs))
2255*7697SMichael.Christensen@Sun.COM 		return (NULL);
2256*7697SMichael.Christensen@Sun.COM 
2257*7697SMichael.Christensen@Sun.COM 	svc = ds_svcs.tbl[idx];
2258*7697SMichael.Christensen@Sun.COM 
2259*7697SMichael.Christensen@Sun.COM 	/* check for a valid service */
2260*7697SMichael.Christensen@Sun.COM 	if (DS_SVC_ISFREE(svc))
2261*7697SMichael.Christensen@Sun.COM 		return (NULL);
2262*7697SMichael.Christensen@Sun.COM 
2263*7697SMichael.Christensen@Sun.COM 	/* make sure the handle is an exact match */
2264*7697SMichael.Christensen@Sun.COM 	if (svc->hdl != hdl)
2265*7697SMichael.Christensen@Sun.COM 		return (NULL);
2266*7697SMichael.Christensen@Sun.COM 
2267*7697SMichael.Christensen@Sun.COM 	return (svc);
2268*7697SMichael.Christensen@Sun.COM }
2269*7697SMichael.Christensen@Sun.COM 
2270*7697SMichael.Christensen@Sun.COM static void
2271*7697SMichael.Christensen@Sun.COM ds_port_reset(ds_port_t *port)
2272*7697SMichael.Christensen@Sun.COM {
2273*7697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2274*7697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&port->lock));
2275*7697SMichael.Christensen@Sun.COM 
2276*7697SMichael.Christensen@Sun.COM 	/* connection went down, mark everything inactive */
2277*7697SMichael.Christensen@Sun.COM 	(void) ds_walk_svcs(ds_svc_unregister, port);
2278*7697SMichael.Christensen@Sun.COM 
2279*7697SMichael.Christensen@Sun.COM 	port->ver_idx = 0;
2280*7697SMichael.Christensen@Sun.COM 	port->ver.major = 0;
2281*7697SMichael.Christensen@Sun.COM 	port->ver.minor = 0;
2282*7697SMichael.Christensen@Sun.COM 	port->state = DS_PORT_LDC_INIT;
2283*7697SMichael.Christensen@Sun.COM }
2284*7697SMichael.Christensen@Sun.COM 
2285*7697SMichael.Christensen@Sun.COM /*
2286*7697SMichael.Christensen@Sun.COM  * Verify that a version array is sorted as expected for the
2287*7697SMichael.Christensen@Sun.COM  * version negotiation to work correctly.
2288*7697SMichael.Christensen@Sun.COM  */
2289*7697SMichael.Christensen@Sun.COM ds_vers_check_t
2290*7697SMichael.Christensen@Sun.COM ds_vers_isvalid(ds_ver_t *vers, int nvers)
2291*7697SMichael.Christensen@Sun.COM {
2292*7697SMichael.Christensen@Sun.COM 	uint16_t	curr_major;
2293*7697SMichael.Christensen@Sun.COM 	uint16_t	curr_minor;
2294*7697SMichael.Christensen@Sun.COM 	int		idx;
2295*7697SMichael.Christensen@Sun.COM 
2296*7697SMichael.Christensen@Sun.COM 	curr_major = vers[0].major;
2297*7697SMichael.Christensen@Sun.COM 	curr_minor = vers[0].minor;
2298*7697SMichael.Christensen@Sun.COM 
2299*7697SMichael.Christensen@Sun.COM 	/*
2300*7697SMichael.Christensen@Sun.COM 	 * Walk the version array, verifying correct ordering.
2301*7697SMichael.Christensen@Sun.COM 	 * The array must be sorted from highest supported
2302*7697SMichael.Christensen@Sun.COM 	 * version to lowest supported version.
2303*7697SMichael.Christensen@Sun.COM 	 */
2304*7697SMichael.Christensen@Sun.COM 	for (idx = 0; idx < nvers; idx++) {
2305*7697SMichael.Christensen@Sun.COM 		if (vers[idx].major > curr_major) {
2306*7697SMichael.Christensen@Sun.COM 			DS_DBG(CE_NOTE, "ds_vers_isvalid: version array has "
2307*7697SMichael.Christensen@Sun.COM 			    " increasing major versions" DS_EOL);
2308*7697SMichael.Christensen@Sun.COM 			return (DS_VERS_INCREASING_MAJOR_ERR);
2309*7697SMichael.Christensen@Sun.COM 		}
2310*7697SMichael.Christensen@Sun.COM 
2311*7697SMichael.Christensen@Sun.COM 		if (vers[idx].major < curr_major) {
2312*7697SMichael.Christensen@Sun.COM 			curr_major = vers[idx].major;
2313*7697SMichael.Christensen@Sun.COM 			curr_minor = vers[idx].minor;
2314*7697SMichael.Christensen@Sun.COM 			continue;
2315*7697SMichael.Christensen@Sun.COM 		}
2316*7697SMichael.Christensen@Sun.COM 
2317*7697SMichael.Christensen@Sun.COM 		if (vers[idx].minor > curr_minor) {
2318*7697SMichael.Christensen@Sun.COM 			DS_DBG(CE_NOTE, "ds_vers_isvalid: version array has "
2319*7697SMichael.Christensen@Sun.COM 			    " increasing minor versions" DS_EOL);
2320*7697SMichael.Christensen@Sun.COM 			return (DS_VERS_INCREASING_MINOR_ERR);
2321*7697SMichael.Christensen@Sun.COM 		}
2322*7697SMichael.Christensen@Sun.COM 
2323*7697SMichael.Christensen@Sun.COM 		curr_minor = vers[idx].minor;
2324*7697SMichael.Christensen@Sun.COM 	}
2325*7697SMichael.Christensen@Sun.COM 
2326*7697SMichael.Christensen@Sun.COM 	return (DS_VERS_OK);
2327*7697SMichael.Christensen@Sun.COM }
2328*7697SMichael.Christensen@Sun.COM 
2329*7697SMichael.Christensen@Sun.COM /*
2330*7697SMichael.Christensen@Sun.COM  * Extended user capability init.
2331*7697SMichael.Christensen@Sun.COM  */
2332*7697SMichael.Christensen@Sun.COM int
2333*7697SMichael.Christensen@Sun.COM ds_ucap_init(ds_capability_t *cap, ds_clnt_ops_t *ops, uint32_t flags,
2334*7697SMichael.Christensen@Sun.COM     int instance, ds_svc_hdl_t *hdlp)
2335*7697SMichael.Christensen@Sun.COM {
2336*7697SMichael.Christensen@Sun.COM 	ds_vers_check_t	status;
2337*7697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
2338*7697SMichael.Christensen@Sun.COM 	int		rv = 0;
2339*7697SMichael.Christensen@Sun.COM 	ds_svc_hdl_t 	lb_hdl, hdl;
2340*7697SMichael.Christensen@Sun.COM 	int		is_loopback;
2341*7697SMichael.Christensen@Sun.COM 	int		is_client;
2342*7697SMichael.Christensen@Sun.COM 
2343*7697SMichael.Christensen@Sun.COM 	/* sanity check the args */
2344*7697SMichael.Christensen@Sun.COM 	if ((cap == NULL) || (ops == NULL)) {
2345*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_NOTE, "%s: invalid arguments" DS_EOL, __func__);
2346*7697SMichael.Christensen@Sun.COM 		return (EINVAL);
2347*7697SMichael.Christensen@Sun.COM 	}
2348*7697SMichael.Christensen@Sun.COM 
2349*7697SMichael.Christensen@Sun.COM 	/* sanity check the capability specifier */
2350*7697SMichael.Christensen@Sun.COM 	if ((cap->svc_id == NULL) || (cap->vers == NULL) || (cap->nvers == 0)) {
2351*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_NOTE, "%s: invalid capability specifier" DS_EOL,
2352*7697SMichael.Christensen@Sun.COM 		    __func__);
2353*7697SMichael.Christensen@Sun.COM 		return (EINVAL);
2354*7697SMichael.Christensen@Sun.COM 	}
2355*7697SMichael.Christensen@Sun.COM 
2356*7697SMichael.Christensen@Sun.COM 	/* sanity check the version array */
2357*7697SMichael.Christensen@Sun.COM 	if ((status = ds_vers_isvalid(cap->vers, cap->nvers)) != DS_VERS_OK) {
2358*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_NOTE, "%s: invalid capability version array "
2359*7697SMichael.Christensen@Sun.COM 		    "for %s service: %s" DS_EOL, __func__, cap->svc_id,
2360*7697SMichael.Christensen@Sun.COM 		    (status == DS_VERS_INCREASING_MAJOR_ERR) ?
2361*7697SMichael.Christensen@Sun.COM 		    "increasing major versions" :
2362*7697SMichael.Christensen@Sun.COM 		    "increasing minor versions");
2363*7697SMichael.Christensen@Sun.COM 		return (EINVAL);
2364*7697SMichael.Christensen@Sun.COM 	}
2365*7697SMichael.Christensen@Sun.COM 
2366*7697SMichael.Christensen@Sun.COM 	/* data and register callbacks are required */
2367*7697SMichael.Christensen@Sun.COM 	if ((ops->ds_data_cb == NULL) || (ops->ds_reg_cb == NULL)) {
2368*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_NOTE, "%s: invalid ops specifier for %s service"
2369*7697SMichael.Christensen@Sun.COM 		    DS_EOL, __func__, cap->svc_id);
2370*7697SMichael.Christensen@Sun.COM 		return (EINVAL);
2371*7697SMichael.Christensen@Sun.COM 	}
2372*7697SMichael.Christensen@Sun.COM 
2373*7697SMichael.Christensen@Sun.COM 	flags &= DSSF_USERFLAGS;
2374*7697SMichael.Christensen@Sun.COM 	is_client = flags & DSSF_ISCLIENT;
2375*7697SMichael.Christensen@Sun.COM 
2376*7697SMichael.Christensen@Sun.COM 	DS_DBG_USR(CE_NOTE, "%s: svc_id='%s', data_cb=0x%lx, cb_arg=0x%lx"
2377*7697SMichael.Christensen@Sun.COM 	    DS_EOL, __func__, cap->svc_id, PTR_TO_LONG(ops->ds_data_cb),
2378*7697SMichael.Christensen@Sun.COM 	    PTR_TO_LONG(ops->cb_arg));
2379*7697SMichael.Christensen@Sun.COM 
2380*7697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
2381*7697SMichael.Christensen@Sun.COM 
2382*7697SMichael.Christensen@Sun.COM 	/* check if the service is already registered */
2383*7697SMichael.Christensen@Sun.COM 	if (i_ds_hdl_lookup(cap->svc_id, is_client, NULL, 1) == 1) {
2384*7697SMichael.Christensen@Sun.COM 		/* already registered */
2385*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_NOTE, "Service '%s'/%s already registered" DS_EOL,
2386*7697SMichael.Christensen@Sun.COM 		    cap->svc_id,
2387*7697SMichael.Christensen@Sun.COM 		    (flags & DSSF_ISCLIENT) ? "client" : "service");
2388*7697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
2389*7697SMichael.Christensen@Sun.COM 		return (EALREADY);
2390*7697SMichael.Christensen@Sun.COM 	}
2391*7697SMichael.Christensen@Sun.COM 
2392*7697SMichael.Christensen@Sun.COM 	svc = ds_alloc_svc();
2393*7697SMichael.Christensen@Sun.COM 	if (is_client) {
2394*7697SMichael.Christensen@Sun.COM 		DS_HDL_SET_ISCLIENT(svc->hdl);
2395*7697SMichael.Christensen@Sun.COM 	}
2396*7697SMichael.Christensen@Sun.COM 
2397*7697SMichael.Christensen@Sun.COM 	svc->state = DS_SVC_FREE;
2398*7697SMichael.Christensen@Sun.COM 	svc->svc_hdl = DS_BADHDL1;
2399*7697SMichael.Christensen@Sun.COM 
2400*7697SMichael.Christensen@Sun.COM 	svc->flags = flags;
2401*7697SMichael.Christensen@Sun.COM 	svc->drvi = instance;
2402*7697SMichael.Christensen@Sun.COM 	svc->drv_psp = NULL;
2403*7697SMichael.Christensen@Sun.COM 
2404*7697SMichael.Christensen@Sun.COM 	/*
2405*7697SMichael.Christensen@Sun.COM 	 * Check for loopback.
2406*7697SMichael.Christensen@Sun.COM 	 */
2407*7697SMichael.Christensen@Sun.COM 	if (i_ds_hdl_lookup(cap->svc_id, is_client == 0, &lb_hdl, 1) == 1) {
2408*7697SMichael.Christensen@Sun.COM 		if ((rv = ds_loopback_set_svc(svc, lb_hdl)) != 0) {
2409*7697SMichael.Christensen@Sun.COM 			cmn_err(CE_WARN, "%s: ds_loopback_set_svc err (%d)"
2410*7697SMichael.Christensen@Sun.COM 			    DS_EOL, __func__, rv);
2411*7697SMichael.Christensen@Sun.COM 			mutex_exit(&ds_svcs.lock);
2412*7697SMichael.Christensen@Sun.COM 			return (rv);
2413*7697SMichael.Christensen@Sun.COM 		}
2414*7697SMichael.Christensen@Sun.COM 		is_loopback = 1;
2415*7697SMichael.Christensen@Sun.COM 	} else
2416*7697SMichael.Christensen@Sun.COM 		is_loopback = 0;
2417*7697SMichael.Christensen@Sun.COM 
2418*7697SMichael.Christensen@Sun.COM 	/* copy over all the client information */
2419*7697SMichael.Christensen@Sun.COM 	(void) memcpy(&svc->cap, cap, sizeof (ds_capability_t));
2420*7697SMichael.Christensen@Sun.COM 
2421*7697SMichael.Christensen@Sun.COM 	/* make a copy of the service name */
2422*7697SMichael.Christensen@Sun.COM 	svc->cap.svc_id = ds_strdup(cap->svc_id);
2423*7697SMichael.Christensen@Sun.COM 
2424*7697SMichael.Christensen@Sun.COM 	/* make a copy of the version array */
2425*7697SMichael.Christensen@Sun.COM 	svc->cap.vers = DS_MALLOC(cap->nvers * sizeof (ds_ver_t));
2426*7697SMichael.Christensen@Sun.COM 	(void) memcpy(svc->cap.vers, cap->vers, cap->nvers * sizeof (ds_ver_t));
2427*7697SMichael.Christensen@Sun.COM 
2428*7697SMichael.Christensen@Sun.COM 	/* copy the client ops vector */
2429*7697SMichael.Christensen@Sun.COM 	(void) memcpy(&svc->ops, ops, sizeof (ds_clnt_ops_t));
2430*7697SMichael.Christensen@Sun.COM 
2431*7697SMichael.Christensen@Sun.COM 	svc->state = DS_SVC_INACTIVE;
2432*7697SMichael.Christensen@Sun.COM 	svc->ver_idx = 0;
2433*7697SMichael.Christensen@Sun.COM 	DS_PORTSET_DUP(svc->avail, ds_allports);
2434*7697SMichael.Christensen@Sun.COM 	DS_PORTSET_SETNULL(svc->tried);
2435*7697SMichael.Christensen@Sun.COM 
2436*7697SMichael.Christensen@Sun.COM 	ds_svcs.nsvcs++;
2437*7697SMichael.Christensen@Sun.COM 
2438*7697SMichael.Christensen@Sun.COM 	hdl = svc->hdl;
2439*7697SMichael.Christensen@Sun.COM 
2440*7697SMichael.Christensen@Sun.COM 	/*
2441*7697SMichael.Christensen@Sun.COM 	 * kludge to allow user callback code to get handle and user args.
2442*7697SMichael.Christensen@Sun.COM 	 * Make sure the callback arg points to the svc structure.
2443*7697SMichael.Christensen@Sun.COM 	 */
2444*7697SMichael.Christensen@Sun.COM 	if ((flags & DSSF_ISUSER) != 0) {
2445*7697SMichael.Christensen@Sun.COM 		ds_cbarg_set_cookie(svc);
2446*7697SMichael.Christensen@Sun.COM 	}
2447*7697SMichael.Christensen@Sun.COM 
2448*7697SMichael.Christensen@Sun.COM 	if (is_loopback) {
2449*7697SMichael.Christensen@Sun.COM 		ds_loopback_register(hdl);
2450*7697SMichael.Christensen@Sun.COM 		ds_loopback_register(lb_hdl);
2451*7697SMichael.Christensen@Sun.COM 	}
2452*7697SMichael.Christensen@Sun.COM 
2453*7697SMichael.Christensen@Sun.COM 	/*
2454*7697SMichael.Christensen@Sun.COM 	 * If this is a client or a non-loopback service provider, send
2455*7697SMichael.Christensen@Sun.COM 	 * out register requests.
2456*7697SMichael.Christensen@Sun.COM 	 */
2457*7697SMichael.Christensen@Sun.COM 	if (!is_loopback || (flags & DSSF_ISCLIENT) != 0)
2458*7697SMichael.Christensen@Sun.COM 		(void) ds_svc_register(svc, NULL);
2459*7697SMichael.Christensen@Sun.COM 
2460*7697SMichael.Christensen@Sun.COM 	if (hdlp) {
2461*7697SMichael.Christensen@Sun.COM 		*hdlp = hdl;
2462*7697SMichael.Christensen@Sun.COM 	}
2463*7697SMichael.Christensen@Sun.COM 
2464*7697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
2465*7697SMichael.Christensen@Sun.COM 
2466*7697SMichael.Christensen@Sun.COM 	DS_DBG_USR(CE_NOTE, "%s: service '%s' assigned handle 0x%09lx" DS_EOL,
2467*7697SMichael.Christensen@Sun.COM 	    __func__, svc->cap.svc_id, hdl);
2468*7697SMichael.Christensen@Sun.COM 
2469*7697SMichael.Christensen@Sun.COM 	return (0);
2470*7697SMichael.Christensen@Sun.COM }
2471*7697SMichael.Christensen@Sun.COM 
2472*7697SMichael.Christensen@Sun.COM /*
2473*7697SMichael.Christensen@Sun.COM  * ds_cap_init interface for previous revision.
2474*7697SMichael.Christensen@Sun.COM  */
2475*7697SMichael.Christensen@Sun.COM int
2476*7697SMichael.Christensen@Sun.COM ds_cap_init(ds_capability_t *cap, ds_clnt_ops_t *ops)
2477*7697SMichael.Christensen@Sun.COM {
2478*7697SMichael.Christensen@Sun.COM 	return (ds_ucap_init(cap, ops, 0, DS_INVALID_INSTANCE, NULL));
2479*7697SMichael.Christensen@Sun.COM }
2480*7697SMichael.Christensen@Sun.COM 
2481*7697SMichael.Christensen@Sun.COM /*
2482*7697SMichael.Christensen@Sun.COM  * Interface for ds_unreg_hdl in lds driver.
2483*7697SMichael.Christensen@Sun.COM  */
2484*7697SMichael.Christensen@Sun.COM int
2485*7697SMichael.Christensen@Sun.COM ds_unreg_hdl(ds_svc_hdl_t hdl)
2486*7697SMichael.Christensen@Sun.COM {
2487*7697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
2488*7697SMichael.Christensen@Sun.COM 	int		is_loopback;
2489*7697SMichael.Christensen@Sun.COM 	ds_svc_hdl_t	lb_hdl;
2490*7697SMichael.Christensen@Sun.COM 
2491*7697SMichael.Christensen@Sun.COM 	DS_DBG_USR(CE_NOTE, "%s: hdl=0x%09lx" DS_EOL, __func__, hdl);
2492*7697SMichael.Christensen@Sun.COM 
2493*7697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
2494*7697SMichael.Christensen@Sun.COM 	if ((svc = ds_get_svc(hdl)) == NULL) {
2495*7697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
2496*7697SMichael.Christensen@Sun.COM 		DS_DBG_USR(CE_NOTE, "%s: unknown hdl: 0x%llx" DS_EOL, __func__,
2497*7697SMichael.Christensen@Sun.COM 		    (u_longlong_t)hdl);
2498*7697SMichael.Christensen@Sun.COM 		return (ENXIO);
2499*7697SMichael.Christensen@Sun.COM 	}
2500*7697SMichael.Christensen@Sun.COM 
2501*7697SMichael.Christensen@Sun.COM 	DS_DBG_USR(CE_NOTE, "%s: svcid='%s', hdl=0x%llx" DS_EOL, __func__,
2502*7697SMichael.Christensen@Sun.COM 	    svc->cap.svc_id, (u_longlong_t)svc->hdl);
2503*7697SMichael.Christensen@Sun.COM 
2504*7697SMichael.Christensen@Sun.COM 	svc->state = DS_SVC_UNREG_PENDING;
2505*7697SMichael.Christensen@Sun.COM 
2506*7697SMichael.Christensen@Sun.COM 	is_loopback = ((svc->flags & DSSF_LOOPBACK) != 0);
2507*7697SMichael.Christensen@Sun.COM 	lb_hdl = svc->svc_hdl;
2508*7697SMichael.Christensen@Sun.COM 
2509*7697SMichael.Christensen@Sun.COM 	if (svc->port) {
2510*7697SMichael.Christensen@Sun.COM 		(void) ds_send_unreg_req(svc);
2511*7697SMichael.Christensen@Sun.COM 	}
2512*7697SMichael.Christensen@Sun.COM 
2513*7697SMichael.Christensen@Sun.COM 	(void) ds_svc_unregister(svc, svc->port);
2514*7697SMichael.Christensen@Sun.COM 
2515*7697SMichael.Christensen@Sun.COM 	ds_delete_svc_entry(svc);
2516*7697SMichael.Christensen@Sun.COM 
2517*7697SMichael.Christensen@Sun.COM 	if (is_loopback) {
2518*7697SMichael.Christensen@Sun.COM 		ds_loopback_unregister(lb_hdl);
2519*7697SMichael.Christensen@Sun.COM 	}
2520*7697SMichael.Christensen@Sun.COM 
2521*7697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
2522*7697SMichael.Christensen@Sun.COM 
2523*7697SMichael.Christensen@Sun.COM 	return (0);
2524*7697SMichael.Christensen@Sun.COM }
2525*7697SMichael.Christensen@Sun.COM 
2526*7697SMichael.Christensen@Sun.COM int
2527*7697SMichael.Christensen@Sun.COM ds_cap_fini(ds_capability_t *cap)
2528*7697SMichael.Christensen@Sun.COM {
2529*7697SMichael.Christensen@Sun.COM 	ds_svc_hdl_t	hdl;
2530*7697SMichael.Christensen@Sun.COM 	int rv;
2531*7697SMichael.Christensen@Sun.COM 	uint_t nhdls = 0;
2532*7697SMichael.Christensen@Sun.COM 
2533*7697SMichael.Christensen@Sun.COM 	DS_DBG(CE_NOTE, "%s: '%s'" DS_EOL, __func__, cap->svc_id);
2534*7697SMichael.Christensen@Sun.COM 	if ((rv = ds_hdl_lookup(cap->svc_id, 0, &hdl, 1, &nhdls)) != 0) {
2535*7697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "%s: ds_hdl_lookup '%s' err (%d)" DS_EOL,
2536*7697SMichael.Christensen@Sun.COM 		    __func__, cap->svc_id, rv);
2537*7697SMichael.Christensen@Sun.COM 		return (rv);
2538*7697SMichael.Christensen@Sun.COM 	}
2539*7697SMichael.Christensen@Sun.COM 
2540*7697SMichael.Christensen@Sun.COM 	if (nhdls == 0) {
2541*7697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "%s: no such service '%s'" DS_EOL,
2542*7697SMichael.Christensen@Sun.COM 		    __func__, cap->svc_id);
2543*7697SMichael.Christensen@Sun.COM 		return (ENXIO);
2544*7697SMichael.Christensen@Sun.COM 	}
2545*7697SMichael.Christensen@Sun.COM 
2546*7697SMichael.Christensen@Sun.COM 	if ((rv = ds_is_my_hdl(hdl, DS_INVALID_INSTANCE)) != 0) {
2547*7697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "%s: ds_is_my_handle err (%d)" DS_EOL, __func__,
2548*7697SMichael.Christensen@Sun.COM 		    rv);
2549*7697SMichael.Christensen@Sun.COM 		return (rv);
2550*7697SMichael.Christensen@Sun.COM 	}
2551*7697SMichael.Christensen@Sun.COM 
2552*7697SMichael.Christensen@Sun.COM 	if ((rv = ds_unreg_hdl(hdl)) != 0) {
2553*7697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "%s: ds_unreg_hdl err (%d)" DS_EOL, __func__,
2554*7697SMichael.Christensen@Sun.COM 		    rv);
2555*7697SMichael.Christensen@Sun.COM 		return (rv);
2556*7697SMichael.Christensen@Sun.COM 	}
2557*7697SMichael.Christensen@Sun.COM 
2558*7697SMichael.Christensen@Sun.COM 	return (0);
2559*7697SMichael.Christensen@Sun.COM }
2560*7697SMichael.Christensen@Sun.COM 
2561*7697SMichael.Christensen@Sun.COM int
2562*7697SMichael.Christensen@Sun.COM ds_cap_send(ds_svc_hdl_t hdl, void *buf, size_t len)
2563*7697SMichael.Christensen@Sun.COM {
2564*7697SMichael.Christensen@Sun.COM 	int		rv;
2565*7697SMichael.Christensen@Sun.COM 	ds_hdr_t	*hdr;
2566*7697SMichael.Christensen@Sun.COM 	caddr_t		msg;
2567*7697SMichael.Christensen@Sun.COM 	size_t		msglen;
2568*7697SMichael.Christensen@Sun.COM 	size_t		hdrlen;
2569*7697SMichael.Christensen@Sun.COM 	caddr_t		payload;
2570*7697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
2571*7697SMichael.Christensen@Sun.COM 	ds_port_t	*port;
2572*7697SMichael.Christensen@Sun.COM 	ds_data_handle_t *data;
2573*7697SMichael.Christensen@Sun.COM 	ds_svc_hdl_t	svc_hdl;
2574*7697SMichael.Christensen@Sun.COM 	int		is_client = 0;
2575*7697SMichael.Christensen@Sun.COM 
2576*7697SMichael.Christensen@Sun.COM 	DS_DBG(CE_NOTE, "%s: hdl: 0x%llx, buf: %lx, len: %ld" DS_EOL, __func__,
2577*7697SMichael.Christensen@Sun.COM 	    (u_longlong_t)hdl, (ulong_t)buf, len);
2578*7697SMichael.Christensen@Sun.COM 
2579*7697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
2580*7697SMichael.Christensen@Sun.COM 
2581*7697SMichael.Christensen@Sun.COM 	if ((svc = ds_get_svc(hdl)) == NULL) {
2582*7697SMichael.Christensen@Sun.COM 		cmn_err(CE_WARN, "%s: invalid handle 0x%llx" DS_EOL, __func__,
2583*7697SMichael.Christensen@Sun.COM 		    (u_longlong_t)hdl);
2584*7697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
2585*7697SMichael.Christensen@Sun.COM 		return (ENXIO);
2586*7697SMichael.Christensen@Sun.COM 	}
2587*7697SMichael.Christensen@Sun.COM 
2588*7697SMichael.Christensen@Sun.COM 	if (svc->state != DS_SVC_ACTIVE) {
2589*7697SMichael.Christensen@Sun.COM 		/* channel is up, but svc is not registered */
2590*7697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "%s: invalid service state 0x%x" DS_EOL,
2591*7697SMichael.Christensen@Sun.COM 		    __func__, svc->state);
2592*7697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
2593*7697SMichael.Christensen@Sun.COM 		return (ENOTCONN);
2594*7697SMichael.Christensen@Sun.COM 	}
2595*7697SMichael.Christensen@Sun.COM 
2596*7697SMichael.Christensen@Sun.COM 	if (svc->flags & DSSF_LOOPBACK) {
2597*7697SMichael.Christensen@Sun.COM 		hdl = svc->svc_hdl;
2598*7697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
2599*7697SMichael.Christensen@Sun.COM 		ds_loopback_send(hdl, buf, len);
2600*7697SMichael.Christensen@Sun.COM 		return (0);
2601*7697SMichael.Christensen@Sun.COM 	}
2602*7697SMichael.Christensen@Sun.COM 
2603*7697SMichael.Christensen@Sun.COM 	if ((port = svc->port) == NULL) {
2604*7697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "%s: service '%s' not associated with a port"
2605*7697SMichael.Christensen@Sun.COM 		    DS_EOL, __func__, svc->cap.svc_id);
2606*7697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
2607*7697SMichael.Christensen@Sun.COM 		return (ECONNRESET);
2608*7697SMichael.Christensen@Sun.COM 	}
2609*7697SMichael.Christensen@Sun.COM 
2610*7697SMichael.Christensen@Sun.COM 	if (svc->flags & DSSF_ISCLIENT) {
2611*7697SMichael.Christensen@Sun.COM 		is_client = 1;
2612*7697SMichael.Christensen@Sun.COM 		svc_hdl = svc->svc_hdl;
2613*7697SMichael.Christensen@Sun.COM 	}
2614*7697SMichael.Christensen@Sun.COM 
2615*7697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
2616*7697SMichael.Christensen@Sun.COM 
2617*7697SMichael.Christensen@Sun.COM 	/* check that the LDC channel is ready */
2618*7697SMichael.Christensen@Sun.COM 	if (port->ldc.state != LDC_UP) {
2619*7697SMichael.Christensen@Sun.COM 		DS_DBG(CE_NOTE, "%s: LDC channel is not up" DS_EOL, __func__);
2620*7697SMichael.Christensen@Sun.COM 		return (ECONNRESET);
2621*7697SMichael.Christensen@Sun.COM 	}
2622*7697SMichael.Christensen@Sun.COM 
2623*7697SMichael.Christensen@Sun.COM 	hdrlen = DS_HDR_SZ + sizeof (ds_data_handle_t);
2624*7697SMichael.Christensen@Sun.COM 
2625*7697SMichael.Christensen@Sun.COM 	msg = DS_MALLOC(len + hdrlen);
2626*7697SMichael.Christensen@Sun.COM 	hdr = (ds_hdr_t *)msg;
2627*7697SMichael.Christensen@Sun.COM 	payload = msg + hdrlen;
2628*7697SMichael.Christensen@Sun.COM 	msglen = len + hdrlen;
2629*7697SMichael.Christensen@Sun.COM 
2630*7697SMichael.Christensen@Sun.COM 	hdr->payload_len = len + sizeof (ds_data_handle_t);
2631*7697SMichael.Christensen@Sun.COM 	hdr->msg_type = DS_DATA;
2632*7697SMichael.Christensen@Sun.COM 
2633*7697SMichael.Christensen@Sun.COM 	data = (ds_data_handle_t *)(msg + DS_HDR_SZ);
2634*7697SMichael.Christensen@Sun.COM 	if (is_client) {
2635*7697SMichael.Christensen@Sun.COM 		data->svc_handle = svc_hdl;
2636*7697SMichael.Christensen@Sun.COM 	} else {
2637*7697SMichael.Christensen@Sun.COM 		data->svc_handle = hdl;
2638*7697SMichael.Christensen@Sun.COM 	}
2639*7697SMichael.Christensen@Sun.COM 
2640*7697SMichael.Christensen@Sun.COM 	if ((buf != NULL) && (len != 0)) {
2641*7697SMichael.Christensen@Sun.COM 		(void) memcpy(payload, buf, len);
2642*7697SMichael.Christensen@Sun.COM 	}
2643*7697SMichael.Christensen@Sun.COM 
2644*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: data>: hdl=0x%llx, len=%ld, "
2645*7697SMichael.Christensen@Sun.COM 	    " payload_len=%d" DS_EOL, PORTID(port), (u_longlong_t)svc->hdl,
2646*7697SMichael.Christensen@Sun.COM 	    msglen, hdr->payload_len);
2647*7697SMichael.Christensen@Sun.COM 	DS_DUMP_MSG(DS_DBG_FLAG_PRCL, msg, msglen);
2648*7697SMichael.Christensen@Sun.COM 
2649*7697SMichael.Christensen@Sun.COM 	if ((rv = ds_send_msg(port, msg, msglen)) != 0) {
2650*7697SMichael.Christensen@Sun.COM 		rv = (rv == EIO) ? ECONNRESET : rv;
2651*7697SMichael.Christensen@Sun.COM 	}
2652*7697SMichael.Christensen@Sun.COM 	DS_FREE(msg, msglen);
2653*7697SMichael.Christensen@Sun.COM 
2654*7697SMichael.Christensen@Sun.COM 	return (rv);
2655*7697SMichael.Christensen@Sun.COM }
2656*7697SMichael.Christensen@Sun.COM 
2657*7697SMichael.Christensen@Sun.COM void
2658*7697SMichael.Christensen@Sun.COM ds_port_common_init(ds_port_t *port)
2659*7697SMichael.Christensen@Sun.COM {
2660*7697SMichael.Christensen@Sun.COM 	int rv;
2661*7697SMichael.Christensen@Sun.COM 
2662*7697SMichael.Christensen@Sun.COM 	if ((port->flags & DS_PORT_MUTEX_INITED) == 0) {
2663*7697SMichael.Christensen@Sun.COM 		mutex_init(&port->lock, NULL, MUTEX_DRIVER, NULL);
2664*7697SMichael.Christensen@Sun.COM 		mutex_init(&port->tx_lock, NULL, MUTEX_DRIVER, NULL);
2665*7697SMichael.Christensen@Sun.COM 		mutex_init(&port->rcv_lock, NULL, MUTEX_DRIVER, NULL);
2666*7697SMichael.Christensen@Sun.COM 		port->flags |= DS_PORT_MUTEX_INITED;
2667*7697SMichael.Christensen@Sun.COM 	}
2668*7697SMichael.Christensen@Sun.COM 
2669*7697SMichael.Christensen@Sun.COM 	port->state = DS_PORT_INIT;
2670*7697SMichael.Christensen@Sun.COM 	DS_PORTSET_ADD(ds_allports, port->id);
2671*7697SMichael.Christensen@Sun.COM 
2672*7697SMichael.Christensen@Sun.COM 	ds_sys_port_init(port);
2673*7697SMichael.Christensen@Sun.COM 
2674*7697SMichael.Christensen@Sun.COM 	mutex_enter(&port->lock);
2675*7697SMichael.Christensen@Sun.COM 	rv = ds_ldc_init(port);
2676*7697SMichael.Christensen@Sun.COM 	mutex_exit(&port->lock);
2677*7697SMichael.Christensen@Sun.COM 
2678*7697SMichael.Christensen@Sun.COM 	/*
2679*7697SMichael.Christensen@Sun.COM 	 * If LDC successfully init'ed, try to kick off protocol for this port.
2680*7697SMichael.Christensen@Sun.COM 	 */
2681*7697SMichael.Christensen@Sun.COM 	if (rv == 0) {
2682*7697SMichael.Christensen@Sun.COM 		ds_handle_up_event(port);
2683*7697SMichael.Christensen@Sun.COM 	}
2684*7697SMichael.Christensen@Sun.COM }
2685*7697SMichael.Christensen@Sun.COM 
2686*7697SMichael.Christensen@Sun.COM void
2687*7697SMichael.Christensen@Sun.COM ds_port_common_fini(ds_port_t *port, int is_fini)
2688*7697SMichael.Christensen@Sun.COM {
2689*7697SMichael.Christensen@Sun.COM 	port->state = DS_PORT_FREE;
2690*7697SMichael.Christensen@Sun.COM 
2691*7697SMichael.Christensen@Sun.COM 	if (is_fini && (port->flags & DS_PORT_MUTEX_INITED) != 0) {
2692*7697SMichael.Christensen@Sun.COM 		mutex_destroy(&port->lock);
2693*7697SMichael.Christensen@Sun.COM 		mutex_destroy(&port->tx_lock);
2694*7697SMichael.Christensen@Sun.COM 		mutex_destroy(&port->rcv_lock);
2695*7697SMichael.Christensen@Sun.COM 		port->flags &= ~DS_PORT_MUTEX_INITED;
2696*7697SMichael.Christensen@Sun.COM 	}
2697*7697SMichael.Christensen@Sun.COM 
2698*7697SMichael.Christensen@Sun.COM 	DS_PORTSET_DEL(ds_allports, port->id);
2699*7697SMichael.Christensen@Sun.COM 
2700*7697SMichael.Christensen@Sun.COM 	ds_sys_port_fini(port);
2701*7697SMichael.Christensen@Sun.COM }
2702*7697SMichael.Christensen@Sun.COM 
2703*7697SMichael.Christensen@Sun.COM /*
2704*7697SMichael.Christensen@Sun.COM  * Initialize table of registered service classes
2705*7697SMichael.Christensen@Sun.COM  */
2706*7697SMichael.Christensen@Sun.COM void
2707*7697SMichael.Christensen@Sun.COM ds_init_svcs_tbl(uint_t nentries)
2708*7697SMichael.Christensen@Sun.COM {
2709*7697SMichael.Christensen@Sun.COM 	int	tblsz;
2710*7697SMichael.Christensen@Sun.COM 
2711*7697SMichael.Christensen@Sun.COM 	ds_svcs.maxsvcs = nentries;
2712*7697SMichael.Christensen@Sun.COM 
2713*7697SMichael.Christensen@Sun.COM 	tblsz = ds_svcs.maxsvcs * sizeof (ds_svc_t *);
2714*7697SMichael.Christensen@Sun.COM 	ds_svcs.tbl = (ds_svc_t **)DS_MALLOC(tblsz);
2715*7697SMichael.Christensen@Sun.COM 
2716*7697SMichael.Christensen@Sun.COM 	ds_svcs.nsvcs = 0;
2717*7697SMichael.Christensen@Sun.COM }
2718*7697SMichael.Christensen@Sun.COM 
2719*7697SMichael.Christensen@Sun.COM /*
2720*7697SMichael.Christensen@Sun.COM  * Find the max and min version supported.
2721*7697SMichael.Christensen@Sun.COM  * Hacked from zeus workspace, support.c
2722*7697SMichael.Christensen@Sun.COM  */
2723*7697SMichael.Christensen@Sun.COM static void
2724*7697SMichael.Christensen@Sun.COM min_max_versions(int num_versions, ds_ver_t *sup_versionsp,
2725*7697SMichael.Christensen@Sun.COM     uint16_t *min_major, uint16_t *max_major)
2726*7697SMichael.Christensen@Sun.COM {
2727*7697SMichael.Christensen@Sun.COM 	int i;
2728*7697SMichael.Christensen@Sun.COM 
2729*7697SMichael.Christensen@Sun.COM 	*min_major = sup_versionsp[0].major;
2730*7697SMichael.Christensen@Sun.COM 	*max_major = *min_major;
2731*7697SMichael.Christensen@Sun.COM 
2732*7697SMichael.Christensen@Sun.COM 	for (i = 1; i < num_versions; i++) {
2733*7697SMichael.Christensen@Sun.COM 		if (sup_versionsp[i].major < *min_major)
2734*7697SMichael.Christensen@Sun.COM 			*min_major = sup_versionsp[i].major;
2735*7697SMichael.Christensen@Sun.COM 
2736*7697SMichael.Christensen@Sun.COM 		if (sup_versionsp[i].major > *max_major)
2737*7697SMichael.Christensen@Sun.COM 			*max_major = sup_versionsp[i].major;
2738*7697SMichael.Christensen@Sun.COM 	}
2739*7697SMichael.Christensen@Sun.COM }
2740*7697SMichael.Christensen@Sun.COM 
2741*7697SMichael.Christensen@Sun.COM /*
2742*7697SMichael.Christensen@Sun.COM  * Check whether the major and minor numbers requested by the peer can be
2743*7697SMichael.Christensen@Sun.COM  * satisfied. If the requested major is supported, true is returned, and the
2744*7697SMichael.Christensen@Sun.COM  * agreed minor is returned in new_minor. If the requested major is not
2745*7697SMichael.Christensen@Sun.COM  * supported, the routine returns false, and the closest major is returned in
2746*7697SMichael.Christensen@Sun.COM  * *new_major, upon which the peer should re-negotiate. The closest major is
2747*7697SMichael.Christensen@Sun.COM  * the just lower that the requested major number.
2748*7697SMichael.Christensen@Sun.COM  *
2749*7697SMichael.Christensen@Sun.COM  * Hacked from zeus workspace, support.c
2750*7697SMichael.Christensen@Sun.COM  */
2751*7697SMichael.Christensen@Sun.COM boolean_t
2752*7697SMichael.Christensen@Sun.COM negotiate_version(int num_versions, ds_ver_t *sup_versionsp,
2753*7697SMichael.Christensen@Sun.COM     uint16_t req_major, uint16_t *new_majorp, uint16_t *new_minorp)
2754*7697SMichael.Christensen@Sun.COM {
2755*7697SMichael.Christensen@Sun.COM 	int i;
2756*7697SMichael.Christensen@Sun.COM 	uint16_t major, lower_major;
2757*7697SMichael.Christensen@Sun.COM 	uint16_t min_major = 0, max_major;
2758*7697SMichael.Christensen@Sun.COM 	boolean_t found_match = B_FALSE;
2759*7697SMichael.Christensen@Sun.COM 
2760*7697SMichael.Christensen@Sun.COM 	min_max_versions(num_versions, sup_versionsp, &min_major, &max_major);
2761*7697SMichael.Christensen@Sun.COM 
2762*7697SMichael.Christensen@Sun.COM 	DS_DBG(CE_NOTE, "negotiate_version: req_major = %u, min = %u, max = %u"
2763*7697SMichael.Christensen@Sun.COM 	    DS_EOL, req_major, min_major, max_major);
2764*7697SMichael.Christensen@Sun.COM 
2765*7697SMichael.Christensen@Sun.COM 	/*
2766*7697SMichael.Christensen@Sun.COM 	 * If the minimum version supported is greater than
2767*7697SMichael.Christensen@Sun.COM 	 * the version requested, return the lowest version
2768*7697SMichael.Christensen@Sun.COM 	 * supported
2769*7697SMichael.Christensen@Sun.COM 	 */
2770*7697SMichael.Christensen@Sun.COM 	if (min_major > req_major) {
2771*7697SMichael.Christensen@Sun.COM 		*new_majorp = min_major;
2772*7697SMichael.Christensen@Sun.COM 		return (B_FALSE);
2773*7697SMichael.Christensen@Sun.COM 	}
2774*7697SMichael.Christensen@Sun.COM 
2775*7697SMichael.Christensen@Sun.COM 	/*
2776*7697SMichael.Christensen@Sun.COM 	 * If the largest version supported is lower than
2777*7697SMichael.Christensen@Sun.COM 	 * the version requested, return the largest version
2778*7697SMichael.Christensen@Sun.COM 	 * supported
2779*7697SMichael.Christensen@Sun.COM 	 */
2780*7697SMichael.Christensen@Sun.COM 	if (max_major < req_major) {
2781*7697SMichael.Christensen@Sun.COM 		*new_majorp = max_major;
2782*7697SMichael.Christensen@Sun.COM 		return (B_FALSE);
2783*7697SMichael.Christensen@Sun.COM 	}
2784*7697SMichael.Christensen@Sun.COM 
2785*7697SMichael.Christensen@Sun.COM 	/*
2786*7697SMichael.Christensen@Sun.COM 	 * Now we know that the requested version lies between the
2787*7697SMichael.Christensen@Sun.COM 	 * min and max versions supported. Check if the requested
2788*7697SMichael.Christensen@Sun.COM 	 * major can be found in supported versions.
2789*7697SMichael.Christensen@Sun.COM 	 */
2790*7697SMichael.Christensen@Sun.COM 	lower_major = min_major;
2791*7697SMichael.Christensen@Sun.COM 	for (i = 0; i < num_versions; i++) {
2792*7697SMichael.Christensen@Sun.COM 		major = sup_versionsp[i].major;
2793*7697SMichael.Christensen@Sun.COM 		if (major == req_major) {
2794*7697SMichael.Christensen@Sun.COM 			found_match = B_TRUE;
2795*7697SMichael.Christensen@Sun.COM 			*new_majorp = req_major;
2796*7697SMichael.Christensen@Sun.COM 			*new_minorp = sup_versionsp[i].minor;
2797*7697SMichael.Christensen@Sun.COM 			break;
2798*7697SMichael.Christensen@Sun.COM 		} else {
2799*7697SMichael.Christensen@Sun.COM 			if ((major < req_major) && (major > lower_major))
2800*7697SMichael.Christensen@Sun.COM 				lower_major = major;
2801*7697SMichael.Christensen@Sun.COM 		}
2802*7697SMichael.Christensen@Sun.COM 	}
2803*7697SMichael.Christensen@Sun.COM 
2804*7697SMichael.Christensen@Sun.COM 	/*
2805*7697SMichael.Christensen@Sun.COM 	 * If no match is found, return the closest available number
2806*7697SMichael.Christensen@Sun.COM 	 */
2807*7697SMichael.Christensen@Sun.COM 	if (!found_match)
2808*7697SMichael.Christensen@Sun.COM 		*new_majorp = lower_major;
2809*7697SMichael.Christensen@Sun.COM 
2810*7697SMichael.Christensen@Sun.COM 	return (found_match);
2811*7697SMichael.Christensen@Sun.COM }
2812*7697SMichael.Christensen@Sun.COM 
2813*7697SMichael.Christensen@Sun.COM /*
2814*7697SMichael.Christensen@Sun.COM  * Specific errno's that are used by ds.c and ldc.c
2815*7697SMichael.Christensen@Sun.COM  */
2816*7697SMichael.Christensen@Sun.COM static struct {
2817*7697SMichael.Christensen@Sun.COM 	int ds_errno;
2818*7697SMichael.Christensen@Sun.COM 	char *estr;
2819*7697SMichael.Christensen@Sun.COM } ds_errno_to_str_tab[] = {
2820*7697SMichael.Christensen@Sun.COM 	{ EIO,		"I/O error" },
2821*7697SMichael.Christensen@Sun.COM 	{ ENXIO,	"No such device or address" },
2822*7697SMichael.Christensen@Sun.COM 	{ EAGAIN,	"Resource temporarily unavailable" },
2823*7697SMichael.Christensen@Sun.COM 	{ ENOMEM,	"Not enough space" },
2824*7697SMichael.Christensen@Sun.COM 	{ EACCES,	"Permission denied" },
2825*7697SMichael.Christensen@Sun.COM 	{ EFAULT,	"Bad address" },
2826*7697SMichael.Christensen@Sun.COM 	{ EBUSY,	"Device busy" },
2827*7697SMichael.Christensen@Sun.COM 	{ EINVAL,	"Invalid argument" },
2828*7697SMichael.Christensen@Sun.COM 	{ ENOSPC,	"No space left on device" },
2829*7697SMichael.Christensen@Sun.COM 	{ ENOMSG,	"No message of desired type" },
2830*7697SMichael.Christensen@Sun.COM #ifdef	ECHRNG
2831*7697SMichael.Christensen@Sun.COM 	{ ECHRNG,	"Channel number out of range" },
2832*7697SMichael.Christensen@Sun.COM #endif
2833*7697SMichael.Christensen@Sun.COM 	{ ENOTSUP,	"Operation not supported" },
2834*7697SMichael.Christensen@Sun.COM 	{ EMSGSIZE,	"Message too long" },
2835*7697SMichael.Christensen@Sun.COM 	{ EADDRINUSE,	"Address already in use" },
2836*7697SMichael.Christensen@Sun.COM 	{ ECONNRESET,	"Connection reset by peer" },
2837*7697SMichael.Christensen@Sun.COM 	{ ENOBUFS,	"No buffer space available" },
2838*7697SMichael.Christensen@Sun.COM 	{ ENOTCONN,	"Socket is not connected" },
2839*7697SMichael.Christensen@Sun.COM 	{ ECONNREFUSED,	"Connection refused" },
2840*7697SMichael.Christensen@Sun.COM 	{ EALREADY,	"Operation already in progress" },
2841*7697SMichael.Christensen@Sun.COM 	{ 0,		NULL },
2842*7697SMichael.Christensen@Sun.COM };
2843*7697SMichael.Christensen@Sun.COM 
2844*7697SMichael.Christensen@Sun.COM char *
2845*7697SMichael.Christensen@Sun.COM ds_errno_to_str(int ds_errno, char *ebuf)
2846*7697SMichael.Christensen@Sun.COM {
2847*7697SMichael.Christensen@Sun.COM 	int i, en;
2848*7697SMichael.Christensen@Sun.COM 
2849*7697SMichael.Christensen@Sun.COM 	for (i = 0; (en = ds_errno_to_str_tab[i].ds_errno) != 0; i++) {
2850*7697SMichael.Christensen@Sun.COM 		if (en == ds_errno) {
2851*7697SMichael.Christensen@Sun.COM 			(void) strcpy(ebuf, ds_errno_to_str_tab[i].estr);
2852*7697SMichael.Christensen@Sun.COM 			return (ebuf);
2853*7697SMichael.Christensen@Sun.COM 		}
2854*7697SMichael.Christensen@Sun.COM 	}
2855*7697SMichael.Christensen@Sun.COM 
2856*7697SMichael.Christensen@Sun.COM 	(void) sprintf(ebuf, "ds_errno (%d)", ds_errno);
2857*7697SMichael.Christensen@Sun.COM 	return (ebuf);
2858*7697SMichael.Christensen@Sun.COM }
2859*7697SMichael.Christensen@Sun.COM 
2860*7697SMichael.Christensen@Sun.COM static void
2861*7697SMichael.Christensen@Sun.COM ds_loopback_register(ds_svc_hdl_t hdl)
2862*7697SMichael.Christensen@Sun.COM {
2863*7697SMichael.Christensen@Sun.COM 	ds_ver_t ds_ver = { 1, 0};
2864*7697SMichael.Christensen@Sun.COM 	ds_svc_t *svc;
2865*7697SMichael.Christensen@Sun.COM 
2866*7697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2867*7697SMichael.Christensen@Sun.COM 	DS_DBG_LOOP(CE_NOTE, "%s: entered hdl: 0x%llx" DS_EOL, __func__,
2868*7697SMichael.Christensen@Sun.COM 	    (u_longlong_t)hdl);
2869*7697SMichael.Christensen@Sun.COM 	if ((svc = ds_get_svc(hdl)) == NULL) {
2870*7697SMichael.Christensen@Sun.COM 		DS_DBG_LOOP(CE_NOTE, "%s: invalid hdl: 0x%llx" DS_EOL, __func__,
2871*7697SMichael.Christensen@Sun.COM 		    (u_longlong_t)hdl);
2872*7697SMichael.Christensen@Sun.COM 		return;
2873*7697SMichael.Christensen@Sun.COM 	}
2874*7697SMichael.Christensen@Sun.COM 	svc->state = DS_SVC_ACTIVE;
2875*7697SMichael.Christensen@Sun.COM 
2876*7697SMichael.Christensen@Sun.COM 	if (svc->ops.ds_reg_cb) {
2877*7697SMichael.Christensen@Sun.COM 		DS_DBG_LOOP(CE_NOTE, "%s: loopback regcb: hdl: 0x%llx" DS_EOL,
2878*7697SMichael.Christensen@Sun.COM 		    __func__, (u_longlong_t)hdl);
2879*7697SMichael.Christensen@Sun.COM 		ds_ver.major = svc->ver.major;
2880*7697SMichael.Christensen@Sun.COM 		ds_ver.minor = svc->ver.minor;
2881*7697SMichael.Christensen@Sun.COM 		(*svc->ops.ds_reg_cb)(svc->ops.cb_arg, &ds_ver, hdl);
2882*7697SMichael.Christensen@Sun.COM 	}
2883*7697SMichael.Christensen@Sun.COM }
2884*7697SMichael.Christensen@Sun.COM 
2885*7697SMichael.Christensen@Sun.COM static void
2886*7697SMichael.Christensen@Sun.COM ds_loopback_unregister(ds_svc_hdl_t hdl)
2887*7697SMichael.Christensen@Sun.COM {
2888*7697SMichael.Christensen@Sun.COM 	ds_svc_t *svc;
2889*7697SMichael.Christensen@Sun.COM 
2890*7697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2891*7697SMichael.Christensen@Sun.COM 	if ((svc = ds_get_svc(hdl)) == NULL) {
2892*7697SMichael.Christensen@Sun.COM 		DS_DBG_LOOP(CE_NOTE, "%s: invalid hdl: 0x%llx" DS_EOL, __func__,
2893*7697SMichael.Christensen@Sun.COM 		    (u_longlong_t)hdl);
2894*7697SMichael.Christensen@Sun.COM 		return;
2895*7697SMichael.Christensen@Sun.COM 	}
2896*7697SMichael.Christensen@Sun.COM 
2897*7697SMichael.Christensen@Sun.COM 	DS_DBG_LOOP(CE_NOTE, "%s: entered hdl: 0x%llx" DS_EOL, __func__,
2898*7697SMichael.Christensen@Sun.COM 	    (u_longlong_t)hdl);
2899*7697SMichael.Christensen@Sun.COM 
2900*7697SMichael.Christensen@Sun.COM 	svc->flags &= ~DSSF_LOOPBACK;
2901*7697SMichael.Christensen@Sun.COM 	svc->svc_hdl = DS_BADHDL2;
2902*7697SMichael.Christensen@Sun.COM 
2903*7697SMichael.Christensen@Sun.COM 	if (svc->ops.ds_unreg_cb) {
2904*7697SMichael.Christensen@Sun.COM 		DS_DBG_LOOP(CE_NOTE, "%s: loopback unregcb: hdl: 0x%llx" DS_EOL,
2905*7697SMichael.Christensen@Sun.COM 		    __func__, (u_longlong_t)hdl);
2906*7697SMichael.Christensen@Sun.COM 		(*svc->ops.ds_unreg_cb)(svc->ops.cb_arg);
2907*7697SMichael.Christensen@Sun.COM 	}
2908*7697SMichael.Christensen@Sun.COM }
2909*7697SMichael.Christensen@Sun.COM 
2910*7697SMichael.Christensen@Sun.COM static void
2911*7697SMichael.Christensen@Sun.COM ds_loopback_send(ds_svc_hdl_t hdl, void *buf, size_t buflen)
2912*7697SMichael.Christensen@Sun.COM {
2913*7697SMichael.Christensen@Sun.COM 	ds_svc_t *svc;
2914*7697SMichael.Christensen@Sun.COM 
2915*7697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
2916*7697SMichael.Christensen@Sun.COM 	if ((svc = ds_get_svc(hdl)) == NULL) {
2917*7697SMichael.Christensen@Sun.COM 		mutex_exit(&ds_svcs.lock);
2918*7697SMichael.Christensen@Sun.COM 		DS_DBG_LOOP(CE_NOTE, "%s: invalid hdl: 0x%llx" DS_EOL, __func__,
2919*7697SMichael.Christensen@Sun.COM 		    (u_longlong_t)hdl);
2920*7697SMichael.Christensen@Sun.COM 		return;
2921*7697SMichael.Christensen@Sun.COM 	}
2922*7697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
2923*7697SMichael.Christensen@Sun.COM 
2924*7697SMichael.Christensen@Sun.COM 	DS_DBG_LOOP(CE_NOTE, "%s: entered hdl: 0x%llx" DS_EOL, __func__,
2925*7697SMichael.Christensen@Sun.COM 	    (u_longlong_t)hdl);
2926*7697SMichael.Christensen@Sun.COM 
2927*7697SMichael.Christensen@Sun.COM 	if (svc->ops.ds_data_cb) {
2928*7697SMichael.Christensen@Sun.COM 		DS_DBG_LOOP(CE_NOTE, "%s: loopback datacb hdl: 0x%llx" DS_EOL,
2929*7697SMichael.Christensen@Sun.COM 		    __func__, (u_longlong_t)hdl);
2930*7697SMichael.Christensen@Sun.COM 		(*svc->ops.ds_data_cb)(svc->ops.cb_arg, buf, buflen);
2931*7697SMichael.Christensen@Sun.COM 	}
2932*7697SMichael.Christensen@Sun.COM }
2933*7697SMichael.Christensen@Sun.COM 
2934*7697SMichael.Christensen@Sun.COM static int
2935*7697SMichael.Christensen@Sun.COM ds_loopback_set_svc(ds_svc_t *svc, ds_svc_hdl_t lb_hdl)
2936*7697SMichael.Christensen@Sun.COM {
2937*7697SMichael.Christensen@Sun.COM 	ds_svc_t *lb_svc;
2938*7697SMichael.Christensen@Sun.COM 
2939*7697SMichael.Christensen@Sun.COM 	if ((lb_svc = ds_get_svc(lb_hdl)) == NULL) {
2940*7697SMichael.Christensen@Sun.COM 		DS_DBG_LOOP(CE_NOTE, "%s: loopback: hdl: 0x%llx invalid" DS_EOL,
2941*7697SMichael.Christensen@Sun.COM 		    __func__, (u_longlong_t)lb_hdl);
2942*7697SMichael.Christensen@Sun.COM 		return (ENXIO);
2943*7697SMichael.Christensen@Sun.COM 	}
2944*7697SMichael.Christensen@Sun.COM 	if (lb_svc->state != DS_SVC_INACTIVE) {
2945*7697SMichael.Christensen@Sun.COM 		DS_DBG_LOOP(CE_NOTE, "%s: loopback inactive: hdl: 0x%llx"
2946*7697SMichael.Christensen@Sun.COM 		    DS_EOL, __func__, (u_longlong_t)lb_hdl);
2947*7697SMichael.Christensen@Sun.COM 		if ((lb_svc->flags & DSSF_ISCLIENT) == 0) {
2948*7697SMichael.Christensen@Sun.COM 			DS_DBG_LOOP(CE_NOTE, "%s: loopback busy hdl: 0x%llx"
2949*7697SMichael.Christensen@Sun.COM 			    DS_EOL, __func__, (u_longlong_t)lb_hdl);
2950*7697SMichael.Christensen@Sun.COM 			return (EBUSY);
2951*7697SMichael.Christensen@Sun.COM 		}
2952*7697SMichael.Christensen@Sun.COM 		lb_svc = ds_svc_clone(lb_svc);
2953*7697SMichael.Christensen@Sun.COM 		DS_DBG_LOOP(CE_NOTE, "%s: loopback clone: ohdl: 0x%llx "
2954*7697SMichael.Christensen@Sun.COM 		    "nhdl: 0x%llx" DS_EOL, __func__, (u_longlong_t)lb_hdl,
2955*7697SMichael.Christensen@Sun.COM 		    (u_longlong_t)lb_svc->hdl);
2956*7697SMichael.Christensen@Sun.COM 	}
2957*7697SMichael.Christensen@Sun.COM 
2958*7697SMichael.Christensen@Sun.COM 	svc->flags |= DSSF_LOOPBACK;
2959*7697SMichael.Christensen@Sun.COM 	svc->svc_hdl = lb_svc->hdl;
2960*7697SMichael.Christensen@Sun.COM 	svc->port = NULL;
2961*7697SMichael.Christensen@Sun.COM 
2962*7697SMichael.Christensen@Sun.COM 	lb_svc->flags |= DSSF_LOOPBACK;
2963*7697SMichael.Christensen@Sun.COM 	lb_svc->svc_hdl = svc->hdl;
2964*7697SMichael.Christensen@Sun.COM 	lb_svc->port = NULL;
2965*7697SMichael.Christensen@Sun.COM 
2966*7697SMichael.Christensen@Sun.COM 	DS_DBG_LOOP(CE_NOTE, "%s: setting loopback between: 0x%llx and 0x%llx"
2967*7697SMichael.Christensen@Sun.COM 	    DS_EOL, __func__, (u_longlong_t)svc->hdl,
2968*7697SMichael.Christensen@Sun.COM 	    (u_longlong_t)lb_svc->hdl);
2969*7697SMichael.Christensen@Sun.COM 	return (0);
2970*7697SMichael.Christensen@Sun.COM }
2971*7697SMichael.Christensen@Sun.COM 
2972*7697SMichael.Christensen@Sun.COM static ds_svc_t *
2973*7697SMichael.Christensen@Sun.COM ds_find_clnt_svc_by_hdl_port(ds_svc_hdl_t hdl, ds_port_t *port)
2974*7697SMichael.Christensen@Sun.COM {
2975*7697SMichael.Christensen@Sun.COM 	int		idx;
2976*7697SMichael.Christensen@Sun.COM 	ds_svc_t	*svc;
2977*7697SMichael.Christensen@Sun.COM 
2978*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: %s looking up clnt hdl: 0x%llx" DS_EOL,
2979*7697SMichael.Christensen@Sun.COM 	    PORTID(port), __func__, (u_longlong_t)hdl);
2980*7697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
2981*7697SMichael.Christensen@Sun.COM 
2982*7697SMichael.Christensen@Sun.COM 	/* walk every table entry */
2983*7697SMichael.Christensen@Sun.COM 	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
2984*7697SMichael.Christensen@Sun.COM 		svc = ds_svcs.tbl[idx];
2985*7697SMichael.Christensen@Sun.COM 		if (DS_SVC_ISFREE(svc))
2986*7697SMichael.Christensen@Sun.COM 			continue;
2987*7697SMichael.Christensen@Sun.COM 		if ((svc->flags & DSSF_ISCLIENT) != 0 &&
2988*7697SMichael.Christensen@Sun.COM 		    svc->svc_hdl == hdl && svc->port == port) {
2989*7697SMichael.Christensen@Sun.COM 			DS_DBG_PRCL(CE_NOTE, "ds@%lx: %s found clnt hdl "
2990*7697SMichael.Christensen@Sun.COM 			    "0x%llx: svc%d" DS_EOL, PORTID(port), __func__,
2991*7697SMichael.Christensen@Sun.COM 			    (u_longlong_t)hdl, (uint_t)DS_HDL2IDX(svc->hdl));
2992*7697SMichael.Christensen@Sun.COM 			return (svc);
2993*7697SMichael.Christensen@Sun.COM 		}
2994*7697SMichael.Christensen@Sun.COM 	}
2995*7697SMichael.Christensen@Sun.COM 	DS_DBG_PRCL(CE_NOTE, "ds@%lx: %s clnt hdl: 0x%llx not found" DS_EOL,
2996*7697SMichael.Christensen@Sun.COM 	    PORTID(port), __func__, (u_longlong_t)hdl);
2997*7697SMichael.Christensen@Sun.COM 
2998*7697SMichael.Christensen@Sun.COM 	return (NULL);
2999*7697SMichael.Christensen@Sun.COM }
3000*7697SMichael.Christensen@Sun.COM 
3001*7697SMichael.Christensen@Sun.COM static ds_svc_t *
3002*7697SMichael.Christensen@Sun.COM ds_svc_clone(ds_svc_t *svc)
3003*7697SMichael.Christensen@Sun.COM {
3004*7697SMichael.Christensen@Sun.COM 	ds_svc_t *newsvc;
3005*7697SMichael.Christensen@Sun.COM 	ds_svc_hdl_t hdl;
3006*7697SMichael.Christensen@Sun.COM 
3007*7697SMichael.Christensen@Sun.COM 	ASSERT(svc->flags & DSSF_ISCLIENT);
3008*7697SMichael.Christensen@Sun.COM 
3009*7697SMichael.Christensen@Sun.COM 	newsvc = ds_alloc_svc();
3010*7697SMichael.Christensen@Sun.COM 
3011*7697SMichael.Christensen@Sun.COM 	/* Can only clone clients for now */
3012*7697SMichael.Christensen@Sun.COM 	hdl = newsvc->hdl | DS_HDL_ISCLIENT_BIT;
3013*7697SMichael.Christensen@Sun.COM 	DS_DBG_USR(CE_NOTE, "%s: cloning client: old hdl: 0x%llx new hdl: "
3014*7697SMichael.Christensen@Sun.COM 	    "0x%llx" DS_EOL, __func__, (u_longlong_t)svc->hdl,
3015*7697SMichael.Christensen@Sun.COM 	    (u_longlong_t)hdl);
3016*7697SMichael.Christensen@Sun.COM 	(void) memcpy(newsvc, svc, sizeof (ds_svc_t));
3017*7697SMichael.Christensen@Sun.COM 	newsvc->hdl = hdl;
3018*7697SMichael.Christensen@Sun.COM 	newsvc->flags &= ~DSSF_LOOPBACK;
3019*7697SMichael.Christensen@Sun.COM 	newsvc->port = NULL;
3020*7697SMichael.Christensen@Sun.COM 	newsvc->svc_hdl = DS_BADHDL2;
3021*7697SMichael.Christensen@Sun.COM 	newsvc->cap.svc_id = ds_strdup(svc->cap.svc_id);
3022*7697SMichael.Christensen@Sun.COM 	newsvc->cap.vers = DS_MALLOC(svc->cap.nvers * sizeof (ds_ver_t));
3023*7697SMichael.Christensen@Sun.COM 	(void) memcpy(newsvc->cap.vers, svc->cap.vers,
3024*7697SMichael.Christensen@Sun.COM 	    svc->cap.nvers * sizeof (ds_ver_t));
3025*7697SMichael.Christensen@Sun.COM 
3026*7697SMichael.Christensen@Sun.COM 	/*
3027*7697SMichael.Christensen@Sun.COM 	 * Kludge to allow lds driver user callbacks to get access to current
3028*7697SMichael.Christensen@Sun.COM 	 * svc structure.  Arg could be index to svc table or some other piece
3029*7697SMichael.Christensen@Sun.COM 	 * of info to get to the svc table entry.
3030*7697SMichael.Christensen@Sun.COM 	 */
3031*7697SMichael.Christensen@Sun.COM 	if (newsvc->flags & DSSF_ISUSER) {
3032*7697SMichael.Christensen@Sun.COM 		newsvc->ops.cb_arg = (ds_cb_arg_t)(newsvc);
3033*7697SMichael.Christensen@Sun.COM 	}
3034*7697SMichael.Christensen@Sun.COM 	return (newsvc);
3035*7697SMichael.Christensen@Sun.COM }
3036*7697SMichael.Christensen@Sun.COM 
3037*7697SMichael.Christensen@Sun.COM /*
3038*7697SMichael.Christensen@Sun.COM  * Internal handle lookup function.
3039*7697SMichael.Christensen@Sun.COM  */
3040*7697SMichael.Christensen@Sun.COM static int
3041*7697SMichael.Christensen@Sun.COM i_ds_hdl_lookup(char *service, uint_t is_client, ds_svc_hdl_t *hdlp,
3042*7697SMichael.Christensen@Sun.COM     uint_t maxhdls)
3043*7697SMichael.Christensen@Sun.COM {
3044*7697SMichael.Christensen@Sun.COM 	int idx;
3045*7697SMichael.Christensen@Sun.COM 	int nhdls = 0;
3046*7697SMichael.Christensen@Sun.COM 	ds_svc_t *svc;
3047*7697SMichael.Christensen@Sun.COM 	uint32_t client_flag = is_client ? DSSF_ISCLIENT : 0;
3048*7697SMichael.Christensen@Sun.COM 
3049*7697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
3050*7697SMichael.Christensen@Sun.COM 
3051*7697SMichael.Christensen@Sun.COM 	for (idx = 0; idx < ds_svcs.maxsvcs && nhdls < maxhdls; idx++) {
3052*7697SMichael.Christensen@Sun.COM 		svc = ds_svcs.tbl[idx];
3053*7697SMichael.Christensen@Sun.COM 		if (DS_SVC_ISFREE(svc))
3054*7697SMichael.Christensen@Sun.COM 			continue;
3055*7697SMichael.Christensen@Sun.COM 		if (strcmp(svc->cap.svc_id, service) == 0 &&
3056*7697SMichael.Christensen@Sun.COM 		    (svc->flags & DSSF_ISCLIENT) == client_flag) {
3057*7697SMichael.Christensen@Sun.COM 			if (hdlp != NULL && nhdls < maxhdls) {
3058*7697SMichael.Christensen@Sun.COM 				hdlp[nhdls] = svc->hdl;
3059*7697SMichael.Christensen@Sun.COM 				nhdls++;
3060*7697SMichael.Christensen@Sun.COM 			} else {
3061*7697SMichael.Christensen@Sun.COM 				nhdls++;
3062*7697SMichael.Christensen@Sun.COM 			}
3063*7697SMichael.Christensen@Sun.COM 		}
3064*7697SMichael.Christensen@Sun.COM 	}
3065*7697SMichael.Christensen@Sun.COM 	return (nhdls);
3066*7697SMichael.Christensen@Sun.COM }
3067*7697SMichael.Christensen@Sun.COM 
3068*7697SMichael.Christensen@Sun.COM /*
3069*7697SMichael.Christensen@Sun.COM  * Interface for ds_hdl_lookup in lds driver.
3070*7697SMichael.Christensen@Sun.COM  */
3071*7697SMichael.Christensen@Sun.COM int
3072*7697SMichael.Christensen@Sun.COM ds_hdl_lookup(char *service, uint_t is_client, ds_svc_hdl_t *hdlp,
3073*7697SMichael.Christensen@Sun.COM     uint_t maxhdls, uint_t *nhdlsp)
3074*7697SMichael.Christensen@Sun.COM {
3075*7697SMichael.Christensen@Sun.COM 	mutex_enter(&ds_svcs.lock);
3076*7697SMichael.Christensen@Sun.COM 	*nhdlsp = i_ds_hdl_lookup(service, is_client, hdlp, maxhdls);
3077*7697SMichael.Christensen@Sun.COM 	mutex_exit(&ds_svcs.lock);
3078*7697SMichael.Christensen@Sun.COM 	return (0);
3079*7697SMichael.Christensen@Sun.COM }
3080*7697SMichael.Christensen@Sun.COM 
3081*7697SMichael.Christensen@Sun.COM static void
3082*7697SMichael.Christensen@Sun.COM ds_portset_del_active_clients(char *service, ds_portset_t *portsp)
3083*7697SMichael.Christensen@Sun.COM {
3084*7697SMichael.Christensen@Sun.COM 	ds_portset_t ports = *portsp;
3085*7697SMichael.Christensen@Sun.COM 	int idx;
3086*7697SMichael.Christensen@Sun.COM 	ds_svc_t *svc;
3087*7697SMichael.Christensen@Sun.COM 
3088*7697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
3089*7697SMichael.Christensen@Sun.COM 
3090*7697SMichael.Christensen@Sun.COM 	for (idx = 0; idx < ds_svcs.maxsvcs; idx++) {
3091*7697SMichael.Christensen@Sun.COM 		svc = ds_svcs.tbl[idx];
3092*7697SMichael.Christensen@Sun.COM 		if (DS_SVC_ISFREE(svc))
3093*7697SMichael.Christensen@Sun.COM 			continue;
3094*7697SMichael.Christensen@Sun.COM 		if (strcmp(svc->cap.svc_id, service) == 0 &&
3095*7697SMichael.Christensen@Sun.COM 		    (svc->flags & DSSF_ISCLIENT) != 0 &&
3096*7697SMichael.Christensen@Sun.COM 		    svc->state != DS_SVC_INACTIVE &&
3097*7697SMichael.Christensen@Sun.COM 		    svc->port != NULL) {
3098*7697SMichael.Christensen@Sun.COM 			DS_PORTSET_DEL(ports, PORTID(svc->port));
3099*7697SMichael.Christensen@Sun.COM 		}
3100*7697SMichael.Christensen@Sun.COM 	}
3101*7697SMichael.Christensen@Sun.COM 	*portsp = ports;
3102*7697SMichael.Christensen@Sun.COM }
3103*7697SMichael.Christensen@Sun.COM 
3104*7697SMichael.Christensen@Sun.COM /*
3105*7697SMichael.Christensen@Sun.COM  * After an UNREG REQ, check if this is a client service with multiple
3106*7697SMichael.Christensen@Sun.COM  * handles.  If it is, then we can eliminate this entry.
3107*7697SMichael.Christensen@Sun.COM  */
3108*7697SMichael.Christensen@Sun.COM static void
3109*7697SMichael.Christensen@Sun.COM ds_check_for_dup_services(ds_svc_t *svc)
3110*7697SMichael.Christensen@Sun.COM {
3111*7697SMichael.Christensen@Sun.COM 	if ((svc->flags & DSSF_ISCLIENT) != 0 &&
3112*7697SMichael.Christensen@Sun.COM 	    svc->state == DS_SVC_INACTIVE &&
3113*7697SMichael.Christensen@Sun.COM 	    i_ds_hdl_lookup(svc->cap.svc_id, 1, NULL, 2) == 2) {
3114*7697SMichael.Christensen@Sun.COM 		ds_delete_svc_entry(svc);
3115*7697SMichael.Christensen@Sun.COM 	}
3116*7697SMichael.Christensen@Sun.COM }
3117*7697SMichael.Christensen@Sun.COM 
3118*7697SMichael.Christensen@Sun.COM static void
3119*7697SMichael.Christensen@Sun.COM ds_delete_svc_entry(ds_svc_t *svc)
3120*7697SMichael.Christensen@Sun.COM {
3121*7697SMichael.Christensen@Sun.COM 	ds_svc_hdl_t tmp_hdl;
3122*7697SMichael.Christensen@Sun.COM 
3123*7697SMichael.Christensen@Sun.COM 	ASSERT(MUTEX_HELD(&ds_svcs.lock));
3124*7697SMichael.Christensen@Sun.COM 
3125*7697SMichael.Christensen@Sun.COM 	/*
3126*7697SMichael.Christensen@Sun.COM 	 * Clear out the structure, but do not deallocate the
3127*7697SMichael.Christensen@Sun.COM 	 * memory. It can be reused for the next registration.
3128*7697SMichael.Christensen@Sun.COM 	 */
3129*7697SMichael.Christensen@Sun.COM 	DS_FREE(svc->cap.svc_id, strlen(svc->cap.svc_id) + 1);
3130*7697SMichael.Christensen@Sun.COM 	DS_FREE(svc->cap.vers, svc->cap.nvers * sizeof (ds_ver_t));
3131*7697SMichael.Christensen@Sun.COM 
3132*7697SMichael.Christensen@Sun.COM 	/* save the handle to prevent reuse */
3133*7697SMichael.Christensen@Sun.COM 	tmp_hdl = svc->hdl;
3134*7697SMichael.Christensen@Sun.COM 	bzero((void *)svc, sizeof (ds_svc_t));
3135*7697SMichael.Christensen@Sun.COM 
3136*7697SMichael.Christensen@Sun.COM 	/* initialize for next use */
3137*7697SMichael.Christensen@Sun.COM 	svc->hdl = tmp_hdl;
3138*7697SMichael.Christensen@Sun.COM 	svc->state = DS_SVC_FREE;
3139*7697SMichael.Christensen@Sun.COM 
3140*7697SMichael.Christensen@Sun.COM 	ds_svcs.nsvcs--;
3141*7697SMichael.Christensen@Sun.COM }
3142