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