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