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 * 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 * 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 * 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 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 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 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 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