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 */
59ds_port_t	ds_ports[DS_MAX_PORTS];
60ds_portset_t	ds_allports;	/* all DS ports in the system */
61ds_portset_t	ds_nullport;	/* allows test against null portset */
62
63/* DS SP port id */
64uint64_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 */
74ds_svcs_t	ds_svcs;
75
76/*
77 * Flag to prevent callbacks while in the middle of DS teardown.
78 */
79boolean_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
91static int ds_retries = DS_DEFAULT_RETRIES;
92static 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 */
102static 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 */
108typedef void (*ds_msg_handler_t)(ds_port_t *port, caddr_t buf, size_t len);
109static void ds_handle_init_req(ds_port_t *port, caddr_t buf, size_t len);
110static void ds_handle_init_ack(ds_port_t *port, caddr_t buf, size_t len);
111static void ds_handle_init_nack(ds_port_t *port, caddr_t buf, size_t len);
112static void ds_handle_reg_req(ds_port_t *port, caddr_t buf, size_t len);
113static void ds_handle_reg_ack(ds_port_t *port, caddr_t buf, size_t len);
114static void ds_handle_reg_nack(ds_port_t *port, caddr_t buf, size_t len);
115static void ds_handle_unreg_req(ds_port_t *port, caddr_t buf, size_t len);
116static void ds_handle_unreg_ack(ds_port_t *port, caddr_t buf, size_t len);
117static void ds_handle_unreg_nack(ds_port_t *port, caddr_t buf, size_t len);
118static void ds_handle_data(ds_port_t *port, caddr_t buf, size_t len);
119static 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 */
129static 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 */
146static int ds_ldc_init(ds_port_t *port);
147
148/* event processing functions */
149static uint_t ds_ldc_cb(uint64_t event, caddr_t arg);
150static int ds_recv_msg(ds_port_t *port, caddr_t msgp, size_t *sizep);
151static void ds_handle_up_event(ds_port_t *port);
152static void ds_handle_down_reset_events(ds_port_t *port);
153static void ds_handle_recv(void *arg);
154static void ds_dispatch_event(void *arg);
155
156/* message sending functions */
157static int ds_send_msg(ds_port_t *port, caddr_t msg, size_t msglen);
158static int ds_send_reg_req(ds_svc_t *svc, ds_port_t *port);
159static void ds_send_unreg_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl);
160static void ds_send_data_nack(ds_port_t *port, ds_svc_hdl_t bad_hdl);
161
162/* walker functions */
163static int ds_svc_isfree(ds_svc_t *svc, void *arg);
164static int ds_svc_unregister(ds_svc_t *svc, void *arg);
165static int ds_svc_port_up(ds_svc_t *svc, void *arg);
166
167/* service utilities */
168static void ds_reset_svc(ds_svc_t *svc, ds_port_t *port);
169static int ds_svc_register_onport(ds_svc_t *svc, ds_port_t *port);
170static int ds_svc_register_onport_walker(ds_svc_t *svc, void *arg);
171static void ds_set_port_ready(ds_port_t *port, uint16_t major, uint16_t minor);
172
173/* port utilities */
174static void ds_port_reset(ds_port_t *port);
175static ldc_status_t ds_update_ldc_state(ds_port_t *port);
176
177/* misc utilities */
178static void min_max_versions(int num_versions, ds_ver_t *sup_versionsp,
179    uint16_t *min_major, uint16_t *max_major);
180
181/* debug */
182static char *decode_ldc_events(uint64_t event, char *buf);
183
184/* loopback */
185static void ds_loopback_register(ds_svc_hdl_t hdl);
186static void ds_loopback_unregister(ds_svc_hdl_t hdl);
187static void ds_loopback_send(ds_svc_hdl_t hdl, void *buf, size_t buflen);
188static int ds_loopback_set_svc(ds_svc_t *svc, ds_capability_t *cap,
189    ds_svc_hdl_t *lb_hdlp);
190
191/* client handling */
192static int i_ds_hdl_lookup(char *service, uint_t is_client, ds_svc_hdl_t *hdlp,
193    uint_t maxhdls);
194static ds_svc_t *ds_find_clnt_svc_by_hdl_port(ds_svc_hdl_t hdl,
195    ds_port_t *port);
196static ds_svc_t *ds_find_svc_by_id_port(char *svc_id, int is_client,
197    ds_port_t *port);
198static ds_svc_t *ds_svc_clone(ds_svc_t *svc);
199static void ds_check_for_dup_services(ds_svc_t *svc);
200static void ds_delete_svc_entry(ds_svc_t *svc);
201
202char *
203ds_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
212void
213ds_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
227static char *
228decode_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
239static ldc_status_t
240ds_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
262static void
263ds_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
289static void
290ds_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
313static uint_t
314ds_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	}
361done:
362	DS_DBG_LDC(CE_NOTE, "ds@%lx: %s: exit" DS_EOL, PORTID(port), __func__);
363
364	return (LDC_SUCCESS);
365}
366
367static int
368ds_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
403int
404ds_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 = NULL;
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 */
446static int
447ds_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
526static void
527ds_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
656static void
657ds_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
682int
683ds_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);
733error:
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
744static void
745ds_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
818static void
819ds_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
864static void
865ds_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
925done:
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
935static ds_svc_t *
936ds_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
965static void
966ds_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
1000do_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
1160static void
1161ds_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
1239done:
1240	mutex_exit(&ds_svcs.lock);
1241}
1242
1243static boolean_t
1244ds_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
1255static void
1256ds_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
1313static void
1314ds_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
1412done:
1413	mutex_exit(&ds_svcs.lock);
1414}
1415
1416static void
1417ds_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
1485static void
1486ds_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
1522static void
1523ds_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
1559static void
1560ds_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
1609static void
1610ds_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 */
1653void
1654ds_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
1697static int
1698ds_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 */
1776int
1777ds_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
1842static void
1843ds_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
1891static void
1892ds_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 */
1956void
1957ds_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 */
2011int
2012ds_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
2031static int
2032ds_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
2051int
2052ds_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
2067int
2068ds_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
2083int
2084ds_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
2107static void
2108ds_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
2124static int
2125ds_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
2168static int
2169ds_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
2180int
2181ds_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
2230static int
2231ds_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
2280static int
2281ds_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
2296static void
2297ds_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
2327ds_svc_t *
2328ds_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
2365found:
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
2380static void
2381ds_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
2396ds_svc_t *
2397ds_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
2426static void
2427ds_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 */
2445ds_vers_check_t
2446ds_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 */
2488int
2489ds_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 */
2635int
2636ds_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 */
2644int
2645ds_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
2686int
2687ds_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
2721int
2722ds_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
2817void
2818ds_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
2846void
2847ds_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 */
2861void
2862ds_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 */
2878static void
2879min_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 */
2906boolean_t
2907negotiate_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 */
2971static 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
2999char *
3000ds_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
3015static void
3016ds_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
3041static void
3042ds_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
3067static void
3068ds_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
3091static int
3092ds_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
3161static ds_svc_t *
3162ds_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
3190static ds_svc_t *
3191ds_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 */
3229static int
3230i_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 */
3260int
3261ds_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 */
3274static void
3275ds_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
3284static void
3285ds_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