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