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_cmp.c 31 * 1394 Services Layer Connection Management Procedures 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 void s1394_cmp_init(s1394_hal_t *hal); 47 static void s1394_cmp_fini(s1394_hal_t *hal); 48 static void s1394_cmp_ompr_recv_read_request(cmd1394_cmd_t *req); 49 static void s1394_cmp_impr_recv_read_request(cmd1394_cmd_t *req); 50 static void s1394_cmp_ompr_recv_lock_request(cmd1394_cmd_t *req); 51 static void s1394_cmp_impr_recv_lock_request(cmd1394_cmd_t *req); 52 static void s1394_cmp_notify_reg_change(s1394_hal_t *hal, t1394_cmp_reg_t reg, 53 s1394_target_t *self); 54 55 56 /* 57 * number of retries to notify registered targets in case target list 58 * changes while the list rwlock is dropped for the time of callback 59 */ 60 uint_t s1394_cmp_notify_retry_cnt = 3; 61 62 s1394_fa_descr_t s1394_cmp_ompr_descr = { 63 IEC61883_CMP_OMPR_ADDR, 64 4, 65 T1394_ADDR_RDENBL | T1394_ADDR_LKENBL, 66 { 67 s1394_cmp_ompr_recv_read_request, 68 NULL, 69 s1394_cmp_ompr_recv_lock_request 70 }, 71 0 72 }; 73 74 s1394_fa_descr_t s1394_cmp_impr_descr = { 75 IEC61883_CMP_IMPR_ADDR, 76 4, 77 T1394_ADDR_RDENBL | T1394_ADDR_LKENBL, 78 { 79 s1394_cmp_impr_recv_read_request, 80 NULL, 81 s1394_cmp_impr_recv_lock_request 82 }, 83 0 84 }; 85 86 87 int 88 s1394_cmp_register(s1394_target_t *target, t1394_cmp_evts_t *evts) 89 { 90 s1394_hal_t *hal = target->on_hal; 91 static t1394_cmp_evts_t default_evts = { NULL, NULL }; 92 93 TNF_PROBE_0_DEBUG(s1394_cmp_register_enter, S1394_TNF_SL_CMP_STACK, ""); 94 95 rw_enter(&hal->target_list_rwlock, RW_WRITER); 96 /* 97 * if registering the first target, claim and initialize addresses 98 */ 99 if (s1394_fa_list_is_empty(hal, S1394_FA_TYPE_CMP)) { 100 if (s1394_fa_claim_addr(hal, S1394_FA_TYPE_CMP_OMPR, 101 &s1394_cmp_ompr_descr) != DDI_SUCCESS) { 102 rw_exit(&hal->target_list_rwlock); 103 return (DDI_FAILURE); 104 } 105 106 if (s1394_fa_claim_addr(hal, S1394_FA_TYPE_CMP_IMPR, 107 &s1394_cmp_impr_descr) != DDI_SUCCESS) { 108 s1394_fa_free_addr(hal, S1394_FA_TYPE_CMP_OMPR); 109 rw_exit(&hal->target_list_rwlock); 110 return (DDI_FAILURE); 111 } 112 113 s1394_cmp_init(hal); 114 } 115 116 /* Add on the target list (we only use one list) */ 117 s1394_fa_list_add(hal, target, S1394_FA_TYPE_CMP); 118 119 if (evts == NULL) { 120 evts = &default_evts; 121 } 122 target->target_fa[S1394_FA_TYPE_CMP].fat_u.cmp.cm_evts = *evts; 123 124 rw_exit(&hal->target_list_rwlock); 125 126 TNF_PROBE_0_DEBUG(s1394_cmp_register_exit, S1394_TNF_SL_CMP_STACK, ""); 127 return (DDI_SUCCESS); 128 } 129 130 int 131 s1394_cmp_unregister(s1394_target_t *target) 132 { 133 s1394_hal_t *hal = target->on_hal; 134 135 TNF_PROBE_0_DEBUG(s1394_cmp_unregister_enter, S1394_TNF_SL_CMP_STACK, 136 ""); 137 138 rw_enter(&hal->target_list_rwlock, RW_WRITER); 139 140 if (s1394_fa_list_remove(hal, target, 141 S1394_FA_TYPE_CMP) == DDI_SUCCESS) { 142 if (s1394_fa_list_is_empty(hal, S1394_FA_TYPE_CMP)) { 143 s1394_fa_free_addr(hal, S1394_FA_TYPE_CMP_OMPR); 144 s1394_fa_free_addr(hal, S1394_FA_TYPE_CMP_IMPR); 145 s1394_cmp_fini(hal); 146 } 147 } else { 148 TNF_PROBE_0(s1394_cmp_unregister_common_error_list, 149 S1394_TNF_SL_CMP_ERROR, ""); 150 } 151 152 rw_exit(&hal->target_list_rwlock); 153 154 TNF_PROBE_0_DEBUG(s1394_cmp_unregister_exit, S1394_TNF_SL_CMP_STACK, 155 ""); 156 return (DDI_SUCCESS); 157 } 158 159 int 160 s1394_cmp_read(s1394_target_t *target, t1394_cmp_reg_t reg, uint32_t *valp) 161 { 162 s1394_hal_t *hal = target->on_hal; 163 s1394_cmp_hal_t *cmp = &hal->hal_cmp; 164 int ret = DDI_FAILURE; 165 166 TNF_PROBE_0_DEBUG(s1394_cmp_read_enter, S1394_TNF_SL_CMP_STACK, ""); 167 168 if (reg == T1394_CMP_OMPR) { 169 rw_enter(&cmp->cmp_ompr_rwlock, RW_READER); 170 *valp = cmp->cmp_ompr_val; 171 rw_exit(&cmp->cmp_ompr_rwlock); 172 ret = DDI_SUCCESS; 173 } else if (reg == T1394_CMP_IMPR) { 174 rw_enter(&cmp->cmp_impr_rwlock, RW_READER); 175 *valp = cmp->cmp_impr_val; 176 rw_exit(&cmp->cmp_impr_rwlock); 177 ret = DDI_SUCCESS; 178 } 179 180 TNF_PROBE_0_DEBUG(s1394_cmp_read_exit, S1394_TNF_SL_CMP_STACK, ""); 181 return (ret); 182 } 183 184 int 185 s1394_cmp_cas(s1394_target_t *target, t1394_cmp_reg_t reg, uint32_t arg_val, 186 uint32_t new_val, uint32_t *old_valp) 187 { 188 s1394_hal_t *hal = target->on_hal; 189 s1394_cmp_hal_t *cmp = &hal->hal_cmp; 190 int ret = DDI_SUCCESS; 191 192 TNF_PROBE_0_DEBUG(s1394_cmp_cas_enter, S1394_TNF_SL_CMP_STACK, ""); 193 194 if (reg == T1394_CMP_OMPR) { 195 rw_enter(&cmp->cmp_ompr_rwlock, RW_WRITER); 196 *old_valp = cmp->cmp_ompr_val; 197 if (cmp->cmp_ompr_val == arg_val) { 198 cmp->cmp_ompr_val = new_val; 199 } 200 rw_exit(&cmp->cmp_ompr_rwlock); 201 } else if (reg == T1394_CMP_IMPR) { 202 rw_enter(&cmp->cmp_impr_rwlock, RW_WRITER); 203 *old_valp = cmp->cmp_impr_val; 204 if (cmp->cmp_impr_val == arg_val) { 205 cmp->cmp_impr_val = new_val; 206 } 207 rw_exit(&cmp->cmp_impr_rwlock); 208 } else { 209 ret = DDI_FAILURE; 210 } 211 212 /* notify other targets */ 213 if (ret == DDI_SUCCESS) { 214 s1394_cmp_notify_reg_change(hal, reg, target); 215 } 216 217 TNF_PROBE_0_DEBUG(s1394_cmp_cas_exit, S1394_TNF_SL_CMP_STACK, ""); 218 return (ret); 219 } 220 221 static void 222 s1394_cmp_init(s1394_hal_t *hal) 223 { 224 s1394_cmp_hal_t *cmp = &hal->hal_cmp; 225 226 rw_init(&cmp->cmp_ompr_rwlock, NULL, RW_DRIVER, NULL); 227 rw_init(&cmp->cmp_impr_rwlock, NULL, RW_DRIVER, NULL); 228 229 cmp->cmp_ompr_val = IEC61883_CMP_OMPR_INIT_VAL; 230 cmp->cmp_impr_val = IEC61883_CMP_IMPR_INIT_VAL; 231 } 232 233 static void 234 s1394_cmp_fini(s1394_hal_t *hal) 235 { 236 s1394_cmp_hal_t *cmp = &hal->hal_cmp; 237 238 rw_destroy(&cmp->cmp_ompr_rwlock); 239 rw_destroy(&cmp->cmp_impr_rwlock); 240 } 241 242 /* 243 * iMPR/oMPR read/lock requests 244 */ 245 static void 246 s1394_cmp_ompr_recv_read_request(cmd1394_cmd_t *req) 247 { 248 s1394_hal_t *hal = req->cmd_callback_arg; 249 s1394_cmp_hal_t *cmp = &hal->hal_cmp; 250 251 TNF_PROBE_0_DEBUG(s1394_cmp_ompr_recv_read_request_enter, 252 S1394_TNF_SL_CMP_STACK, ""); 253 254 if (req->cmd_type != CMD1394_ASYNCH_RD_QUAD) { 255 req->cmd_result = IEEE1394_RESP_TYPE_ERROR; 256 } else { 257 rw_enter(&cmp->cmp_ompr_rwlock, RW_READER); 258 req->cmd_u.q.quadlet_data = cmp->cmp_ompr_val; 259 rw_exit(&cmp->cmp_ompr_rwlock); 260 req->cmd_result = IEEE1394_RESP_COMPLETE; 261 } 262 263 (void) s1394_send_response(hal, req); 264 265 TNF_PROBE_0_DEBUG(s1394_cmp_ompr_recv_read_request_exit, 266 S1394_TNF_SL_CMP_STACK, ""); 267 } 268 269 static void 270 s1394_cmp_impr_recv_read_request(cmd1394_cmd_t *req) 271 { 272 s1394_hal_t *hal = req->cmd_callback_arg; 273 s1394_cmp_hal_t *cmp = &hal->hal_cmp; 274 275 TNF_PROBE_0_DEBUG(s1394_cmp_impr_recv_read_request_enter, 276 S1394_TNF_SL_CMP_STACK, ""); 277 278 if (req->cmd_type != CMD1394_ASYNCH_RD_QUAD) { 279 req->cmd_result = IEEE1394_RESP_TYPE_ERROR; 280 } else { 281 rw_enter(&cmp->cmp_impr_rwlock, RW_READER); 282 req->cmd_u.q.quadlet_data = cmp->cmp_impr_val; 283 rw_exit(&cmp->cmp_impr_rwlock); 284 req->cmd_result = IEEE1394_RESP_COMPLETE; 285 } 286 287 (void) s1394_send_response(hal, req); 288 289 TNF_PROBE_0_DEBUG(s1394_cmp_impr_recv_read_request_exit, 290 S1394_TNF_SL_CMP_STACK, ""); 291 } 292 293 static void 294 s1394_cmp_ompr_recv_lock_request(cmd1394_cmd_t *req) 295 { 296 s1394_hal_t *hal = req->cmd_callback_arg; 297 s1394_cmp_hal_t *cmp = &hal->hal_cmp; 298 boolean_t notify = B_TRUE; 299 300 TNF_PROBE_0_DEBUG(s1394_cmp_ompr_recv_lock_request_enter, 301 S1394_TNF_SL_CMP_STACK, ""); 302 303 if ((req->cmd_type != CMD1394_ASYNCH_LOCK_32) || 304 (req->cmd_u.l32.lock_type != CMD1394_LOCK_COMPARE_SWAP)) { 305 req->cmd_result = IEEE1394_RESP_TYPE_ERROR; 306 notify = B_FALSE; 307 } else { 308 rw_enter(&cmp->cmp_ompr_rwlock, RW_WRITER); 309 req->cmd_u.l32.old_value = cmp->cmp_ompr_val; 310 if (cmp->cmp_ompr_val == req->cmd_u.l32.arg_value) { 311 /* write only allowed bits */ 312 cmp->cmp_ompr_val = (req->cmd_u.l32.data_value & 313 IEC61883_CMP_OMPR_LOCK_MASK) | 314 (cmp->cmp_ompr_val & ~IEC61883_CMP_OMPR_LOCK_MASK); 315 } 316 rw_exit(&cmp->cmp_ompr_rwlock); 317 req->cmd_result = IEEE1394_RESP_COMPLETE; 318 } 319 320 (void) s1394_send_response(hal, req); 321 322 /* notify all targets */ 323 if (notify) { 324 s1394_cmp_notify_reg_change(hal, T1394_CMP_OMPR, NULL); 325 } 326 327 TNF_PROBE_0_DEBUG(s1394_cmp_ompr_recv_lock_request_exit, 328 S1394_TNF_SL_CMP_STACK, ""); 329 } 330 331 static void 332 s1394_cmp_impr_recv_lock_request(cmd1394_cmd_t *req) 333 { 334 s1394_hal_t *hal = req->cmd_callback_arg; 335 s1394_cmp_hal_t *cmp = &hal->hal_cmp; 336 boolean_t notify = B_TRUE; 337 338 TNF_PROBE_0_DEBUG(s1394_cmp_impr_recv_lock_request_enter, 339 S1394_TNF_SL_CMP_STACK, ""); 340 341 if ((req->cmd_type != CMD1394_ASYNCH_LOCK_32) || 342 (req->cmd_u.l32.lock_type != CMD1394_LOCK_COMPARE_SWAP)) { 343 req->cmd_result = IEEE1394_RESP_TYPE_ERROR; 344 notify = B_FALSE; 345 } else { 346 rw_enter(&cmp->cmp_impr_rwlock, RW_WRITER); 347 req->cmd_u.l32.old_value = cmp->cmp_impr_val; 348 if (cmp->cmp_impr_val == req->cmd_u.l32.arg_value) { 349 /* write only allowed bits */ 350 cmp->cmp_impr_val = (req->cmd_u.l32.data_value & 351 IEC61883_CMP_IMPR_LOCK_MASK) | 352 (cmp->cmp_impr_val & ~IEC61883_CMP_IMPR_LOCK_MASK); 353 } 354 rw_exit(&cmp->cmp_impr_rwlock); 355 req->cmd_result = IEEE1394_RESP_COMPLETE; 356 } 357 358 (void) s1394_send_response(hal, req); 359 360 /* notify all targets */ 361 if (notify) { 362 s1394_cmp_notify_reg_change(hal, T1394_CMP_IMPR, NULL); 363 } 364 365 TNF_PROBE_0_DEBUG(s1394_cmp_impr_recv_lock_request_exit, 366 S1394_TNF_SL_CMP_STACK, ""); 367 } 368 369 /* 370 * Notify registered targets except 'self' about register value change 371 */ 372 static void 373 s1394_cmp_notify_reg_change(s1394_hal_t *hal, t1394_cmp_reg_t reg, 374 s1394_target_t *self) 375 { 376 s1394_target_t *target; 377 s1394_fa_target_t *fat; 378 uint_t saved_gen; 379 int num_retries = 0; 380 void (*cb)(opaque_t, t1394_cmp_reg_t); 381 opaque_t arg; 382 383 TNF_PROBE_0_DEBUG(s1394_cmp_notify_reg_change_enter, 384 S1394_TNF_SL_CMP_STACK, ""); 385 386 rw_enter(&hal->target_list_rwlock, RW_READER); 387 388 start: 389 target = hal->hal_fa[S1394_FA_TYPE_CMP].fal_head; 390 391 for (; target; target = fat->fat_next) { 392 fat = &target->target_fa[S1394_FA_TYPE_CMP]; 393 394 /* 395 * even if the target list changes when the lock is dropped, 396 * comparing with self is safe because the target should 397 * not unregister until all CMP operations are completed 398 */ 399 if (target == self) { 400 continue; 401 } 402 403 cb = fat->fat_u.cmp.cm_evts.cmp_reg_change; 404 if (cb == NULL) { 405 continue; 406 } 407 arg = fat->fat_u.cmp.cm_evts.cmp_arg; 408 409 saved_gen = s1394_fa_list_gen(hal, S1394_FA_TYPE_CMP); 410 411 rw_exit(&hal->target_list_rwlock); 412 cb(arg, reg); 413 rw_enter(&hal->target_list_rwlock, RW_READER); 414 415 /* 416 * List could change while we dropped the lock. In such 417 * case, start all over again, because missing a register 418 * change can have more serious consequences for a 419 * target than receiving same notification more than once 420 */ 421 if (saved_gen != s1394_fa_list_gen(hal, S1394_FA_TYPE_CMP)) { 422 TNF_PROBE_2(s1394_cmp_notify_reg_change_error, 423 S1394_TNF_SL_CMP_ERROR, "", 424 tnf_string, msg, "list gen changed", 425 tnf_opaque, num_retries, num_retries); 426 if (++num_retries <= s1394_cmp_notify_retry_cnt) { 427 goto start; 428 } else { 429 break; 430 } 431 } 432 } 433 434 rw_exit(&hal->target_list_rwlock); 435 436 TNF_PROBE_0_DEBUG(s1394_cmp_notify_reg_change_exit, 437 S1394_TNF_SL_CMP_STACK, ""); 438 } 439