1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2002 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * s1394_fcp.c 31 * 1394 Services Layer FCP Support Routines 32 */ 33 34 #include <sys/conf.h> 35 #include <sys/ddi.h> 36 #include <sys/sunddi.h> 37 #include <sys/cmn_err.h> 38 #include <sys/types.h> 39 #include <sys/kmem.h> 40 #include <sys/tnf_probe.h> 41 42 #include <sys/1394/t1394.h> 43 #include <sys/1394/s1394.h> 44 #include <sys/1394/h1394.h> 45 46 static int s1394_fcp_register_common(s1394_target_t *target, 47 t1394_fcp_evts_t *evts, s1394_fa_type_t type, s1394_fa_descr_t *descr); 48 static int s1394_fcp_unregister_common(s1394_target_t *target, 49 s1394_fa_type_t type); 50 static void s1394_fcp_resp_recv_write_request(cmd1394_cmd_t *req); 51 static void s1394_fcp_cmd_recv_write_request(cmd1394_cmd_t *req); 52 static void s1394_fcp_recv_write_request(cmd1394_cmd_t *req, 53 s1394_fa_type_t type); 54 static void s1394_fcp_recv_write_unclaimed(s1394_hal_t *hal, 55 cmd1394_cmd_t *req); 56 57 58 /* 59 * number of retries to notify registered targets in case target list 60 * changes while the list rwlock is dropped for the time of callback 61 */ 62 uint_t s1394_fcp_notify_retry_cnt = 3; 63 64 s1394_fa_descr_t s1394_fcp_ctl_descr = { 65 IEC61883_FCP_RESP_ADDR, 66 IEC61883_FCP_RESP_SIZE, 67 T1394_ADDR_WRENBL, 68 { NULL, s1394_fcp_resp_recv_write_request, NULL }, 69 IEC61883_FCP_CMD_ADDR 70 }; 71 72 s1394_fa_descr_t s1394_fcp_tgt_descr = { 73 IEC61883_FCP_CMD_ADDR, 74 IEC61883_FCP_CMD_SIZE, 75 T1394_ADDR_WRENBL, 76 { NULL, s1394_fcp_cmd_recv_write_request, NULL }, 77 IEC61883_FCP_RESP_ADDR 78 }; 79 80 81 int 82 s1394_fcp_hal_init(s1394_hal_t *hal) 83 { 84 int ret = DDI_SUCCESS; 85 86 TNF_PROBE_0_DEBUG(s1394_fcp_hal_init_enter, S1394_TNF_SL_FCP_STACK, ""); 87 88 if ((ddi_prop_exists(DDI_DEV_T_ANY, hal->halinfo.dip, DDI_PROP_DONTPASS, 89 "h1394-fcp-claim-on-demand")) == 0) { 90 /* if not on-demand, claim addresses now */ 91 ret = s1394_fa_claim_addr(hal, S1394_FA_TYPE_FCP_CTL, 92 &s1394_fcp_ctl_descr); 93 if (ret == DDI_SUCCESS) { 94 ret = s1394_fa_claim_addr(hal, S1394_FA_TYPE_FCP_TGT, 95 &s1394_fcp_tgt_descr); 96 if (ret != DDI_SUCCESS) { 97 s1394_fa_free_addr(hal, S1394_FA_TYPE_FCP_CTL); 98 } 99 } 100 } 101 102 TNF_PROBE_0_DEBUG(s1394_fcp_hal_init_exit, S1394_TNF_SL_FCP_STACK, ""); 103 return (ret); 104 } 105 106 int 107 s1394_fcp_register_ctl(s1394_target_t *target, t1394_fcp_evts_t *evts) 108 { 109 return (s1394_fcp_register_common(target, evts, S1394_FA_TYPE_FCP_CTL, 110 &s1394_fcp_ctl_descr)); 111 } 112 113 int 114 s1394_fcp_register_tgt(s1394_target_t *target, t1394_fcp_evts_t *evts) 115 { 116 return (s1394_fcp_register_common(target, evts, S1394_FA_TYPE_FCP_TGT, 117 &s1394_fcp_tgt_descr)); 118 } 119 120 int 121 s1394_fcp_unregister_ctl(s1394_target_t *target) 122 { 123 return (s1394_fcp_unregister_common(target, S1394_FA_TYPE_FCP_CTL)); 124 } 125 126 int 127 s1394_fcp_unregister_tgt(s1394_target_t *target) 128 { 129 return (s1394_fcp_unregister_common(target, S1394_FA_TYPE_FCP_TGT)); 130 } 131 132 133 static int 134 s1394_fcp_register_common(s1394_target_t *target, t1394_fcp_evts_t *evts, 135 s1394_fa_type_t type, s1394_fa_descr_t *descr) 136 { 137 s1394_hal_t *hal = target->on_hal; 138 s1394_fcp_target_t *fcp; 139 140 rw_enter(&hal->target_list_rwlock, RW_WRITER); 141 142 if (s1394_fa_list_is_empty(hal, type)) { 143 if (s1394_fa_claim_addr(hal, type, descr) != DDI_SUCCESS) { 144 rw_exit(&hal->target_list_rwlock); 145 return (DDI_FAILURE); 146 } 147 } 148 149 /* Add on the target list */ 150 s1394_fa_list_add(hal, target, type); 151 152 fcp = &target->target_fa[type].fat_u.fcp; 153 fcp->fc_evts = *evts; 154 155 rw_exit(&hal->target_list_rwlock); 156 157 return (DDI_SUCCESS); 158 } 159 160 static int 161 s1394_fcp_unregister_common(s1394_target_t *target, s1394_fa_type_t type) 162 { 163 s1394_hal_t *hal = target->on_hal; 164 int result; 165 166 rw_enter(&hal->target_list_rwlock, RW_WRITER); 167 168 result = s1394_fa_list_remove(hal, target, type); 169 if (result == DDI_SUCCESS) { 170 if (s1394_fa_list_is_empty(hal, type)) { 171 s1394_fa_free_addr(hal, type); 172 } 173 } else { 174 TNF_PROBE_0(s1394_fcp_unregister_common_error_list, 175 S1394_TNF_SL_FCP_ERROR, ""); 176 } 177 178 rw_exit(&hal->target_list_rwlock); 179 180 return (result); 181 } 182 183 /* 184 * s1394_fcp_write_check_cmd() 185 * Check if an FCP command is formed correctly; 186 * set cmd_result and return DDI_FAILURE if not. 187 */ 188 int 189 s1394_fcp_write_check_cmd(cmd1394_cmd_t *cmd) 190 { 191 int len; 192 193 /* 4-byte writes must be quadlet writes */ 194 if (cmd->cmd_type == CMD1394_ASYNCH_WR_BLOCK) { 195 len = cmd->cmd_u.b.blk_length; 196 if (len == 4) { 197 cmd->cmd_result = CMD1394_ETYPE_ERROR; 198 TNF_PROBE_0(t1394_write_error_type, 199 S1394_TNF_SL_FCP_ERROR, ""); 200 return (DDI_FAILURE); 201 } 202 } else { 203 len = 4; 204 } 205 206 /* 207 * request must be within FCP range. we avoid extra checks by 208 * using the fact that command and response are of the same size 209 */ 210 if ((cmd->cmd_addr & IEEE1394_ADDR_OFFSET_MASK) + len > 211 IEC61883_FCP_CMD_SIZE) { 212 cmd->cmd_result = CMD1394_EADDRESS_ERROR; 213 TNF_PROBE_0(t1394_write_error_addr, S1394_TNF_SL_FCP_ERROR, ""); 214 return (DDI_FAILURE); 215 } 216 217 /* some options don't make sense for FCP commands */ 218 if (cmd->cmd_options & CMD1394_OVERRIDE_ADDR) { 219 cmd->cmd_result = CMD1394_EINVALID_COMMAND; 220 TNF_PROBE_0(t1394_write_error_opt, S1394_TNF_SL_FCP_ERROR, ""); 221 return (DDI_FAILURE); 222 } 223 224 return (DDI_SUCCESS); 225 } 226 227 static void 228 s1394_fcp_resp_recv_write_request(cmd1394_cmd_t *req) 229 { 230 s1394_fcp_recv_write_request(req, S1394_FA_TYPE_FCP_CTL); 231 } 232 233 static void 234 s1394_fcp_cmd_recv_write_request(cmd1394_cmd_t *req) 235 { 236 s1394_fcp_recv_write_request(req, S1394_FA_TYPE_FCP_TGT); 237 } 238 239 /* 240 * s1394_fcp_recv_write_request() 241 * Common write request handler 242 */ 243 static void 244 s1394_fcp_recv_write_request(cmd1394_cmd_t *req, s1394_fa_type_t type) 245 { 246 s1394_hal_t *hal = (s1394_hal_t *)req->cmd_callback_arg; 247 s1394_target_t *target; 248 s1394_fa_target_t *fat; 249 uint_t saved_gen; 250 int num_retries = 0; 251 int (*cb)(cmd1394_cmd_t *req); 252 boolean_t restored = B_FALSE; 253 int ret = T1394_REQ_UNCLAIMED; 254 255 TNF_PROBE_0_DEBUG(s1394_fcp_recv_write_request_enter, 256 S1394_TNF_SL_FCP_STACK, ""); 257 258 rw_enter(&hal->target_list_rwlock, RW_READER); 259 260 start: 261 target = hal->hal_fa[type].fal_head; 262 263 if (target) { 264 s1394_fa_restore_cmd(hal, req); 265 restored = B_TRUE; 266 267 /* Find a target that claims the request */ 268 do { 269 fat = &target->target_fa[type]; 270 271 cb = fat->fat_u.fcp.fc_evts.fcp_write_request; 272 if (cb == NULL) { 273 continue; 274 } 275 req->cmd_callback_arg = fat->fat_u.fcp.fc_evts.fcp_arg; 276 277 saved_gen = s1394_fa_list_gen(hal, type); 278 279 rw_exit(&hal->target_list_rwlock); 280 ret = cb(req); 281 rw_enter(&hal->target_list_rwlock, RW_READER); 282 283 if (ret == T1394_REQ_CLAIMED) { 284 break; 285 } 286 287 /* 288 * List could change while we dropped the lock. In such 289 * case, start all over again, because missing a write 290 * request can have more serious consequences for a 291 * target than receiving same request more than once 292 */ 293 if (saved_gen != s1394_fa_list_gen(hal, type)) { 294 TNF_PROBE_2(s1394_fcp_recv_write_request_error, 295 S1394_TNF_SL_FCP_ERROR, "", 296 tnf_string, msg, "list gen changed", 297 tnf_opaque, num_retries, num_retries); 298 num_retries++; 299 if (num_retries <= s1394_fcp_notify_retry_cnt) { 300 goto start; 301 } else { 302 break; 303 } 304 } 305 306 target = fat->fat_next; 307 } while (target != NULL); 308 } 309 310 rw_exit(&hal->target_list_rwlock); 311 312 if (ret != T1394_REQ_CLAIMED) { 313 TNF_PROBE_0(s1394_fcp_recv_write_request_error_unclaimed, 314 S1394_TNF_SL_FCP_ERROR, ""); 315 if (restored) { 316 s1394_fa_convert_cmd(hal, req); 317 } 318 s1394_fcp_recv_write_unclaimed(hal, req); 319 } 320 321 TNF_PROBE_0_DEBUG(s1394_fcp_recv_write_request_exit, 322 S1394_TNF_SL_FCP_STACK, ""); 323 } 324 325 /* 326 * none of the targets claimed the request - send an appropriate response 327 */ 328 static void 329 s1394_fcp_recv_write_unclaimed(s1394_hal_t *hal, cmd1394_cmd_t *req) 330 { 331 req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR; 332 (void) s1394_send_response(hal, req); 333 } 334