Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / usr / src / uts / common / io / 1394 / s1394_cmp.c
blob37e25981d1c1be75b08072e71aef3d9944183144
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
23 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * s1394_cmp.c
31 * 1394 Services Layer Connection Management Procedures Support Routines
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>
42 #include <sys/1394/t1394.h>
43 #include <sys/1394/s1394.h>
44 #include <sys/1394/h1394.h>
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);
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
60 uint_t s1394_cmp_notify_retry_cnt = 3;
62 s1394_fa_descr_t s1394_cmp_ompr_descr = {
63 IEC61883_CMP_OMPR_ADDR,
65 T1394_ADDR_RDENBL | T1394_ADDR_LKENBL,
67 s1394_cmp_ompr_recv_read_request,
68 NULL,
69 s1394_cmp_ompr_recv_lock_request
74 s1394_fa_descr_t s1394_cmp_impr_descr = {
75 IEC61883_CMP_IMPR_ADDR,
77 T1394_ADDR_RDENBL | T1394_ADDR_LKENBL,
79 s1394_cmp_impr_recv_read_request,
80 NULL,
81 s1394_cmp_impr_recv_lock_request
87 int
88 s1394_cmp_register(s1394_target_t *target, t1394_cmp_evts_t *evts)
90 s1394_hal_t *hal = target->on_hal;
91 static t1394_cmp_evts_t default_evts = { NULL, NULL };
93 rw_enter(&hal->target_list_rwlock, RW_WRITER);
95 * if registering the first target, claim and initialize addresses
97 if (s1394_fa_list_is_empty(hal, S1394_FA_TYPE_CMP)) {
98 if (s1394_fa_claim_addr(hal, S1394_FA_TYPE_CMP_OMPR,
99 &s1394_cmp_ompr_descr) != DDI_SUCCESS) {
100 rw_exit(&hal->target_list_rwlock);
101 return (DDI_FAILURE);
104 if (s1394_fa_claim_addr(hal, S1394_FA_TYPE_CMP_IMPR,
105 &s1394_cmp_impr_descr) != DDI_SUCCESS) {
106 s1394_fa_free_addr(hal, S1394_FA_TYPE_CMP_OMPR);
107 rw_exit(&hal->target_list_rwlock);
108 return (DDI_FAILURE);
111 s1394_cmp_init(hal);
114 /* Add on the target list (we only use one list) */
115 s1394_fa_list_add(hal, target, S1394_FA_TYPE_CMP);
117 if (evts == NULL) {
118 evts = &default_evts;
120 target->target_fa[S1394_FA_TYPE_CMP].fat_u.cmp.cm_evts = *evts;
122 rw_exit(&hal->target_list_rwlock);
124 return (DDI_SUCCESS);
128 s1394_cmp_unregister(s1394_target_t *target)
130 s1394_hal_t *hal = target->on_hal;
132 rw_enter(&hal->target_list_rwlock, RW_WRITER);
134 if (s1394_fa_list_remove(hal, target,
135 S1394_FA_TYPE_CMP) == DDI_SUCCESS) {
136 if (s1394_fa_list_is_empty(hal, S1394_FA_TYPE_CMP)) {
137 s1394_fa_free_addr(hal, S1394_FA_TYPE_CMP_OMPR);
138 s1394_fa_free_addr(hal, S1394_FA_TYPE_CMP_IMPR);
139 s1394_cmp_fini(hal);
141 } else {
144 rw_exit(&hal->target_list_rwlock);
146 return (DDI_SUCCESS);
150 s1394_cmp_read(s1394_target_t *target, t1394_cmp_reg_t reg, uint32_t *valp)
152 s1394_hal_t *hal = target->on_hal;
153 s1394_cmp_hal_t *cmp = &hal->hal_cmp;
154 int ret = DDI_FAILURE;
156 if (reg == T1394_CMP_OMPR) {
157 rw_enter(&cmp->cmp_ompr_rwlock, RW_READER);
158 *valp = cmp->cmp_ompr_val;
159 rw_exit(&cmp->cmp_ompr_rwlock);
160 ret = DDI_SUCCESS;
161 } else if (reg == T1394_CMP_IMPR) {
162 rw_enter(&cmp->cmp_impr_rwlock, RW_READER);
163 *valp = cmp->cmp_impr_val;
164 rw_exit(&cmp->cmp_impr_rwlock);
165 ret = DDI_SUCCESS;
168 return (ret);
172 s1394_cmp_cas(s1394_target_t *target, t1394_cmp_reg_t reg, uint32_t arg_val,
173 uint32_t new_val, uint32_t *old_valp)
175 s1394_hal_t *hal = target->on_hal;
176 s1394_cmp_hal_t *cmp = &hal->hal_cmp;
177 int ret = DDI_SUCCESS;
179 if (reg == T1394_CMP_OMPR) {
180 rw_enter(&cmp->cmp_ompr_rwlock, RW_WRITER);
181 *old_valp = cmp->cmp_ompr_val;
182 if (cmp->cmp_ompr_val == arg_val) {
183 cmp->cmp_ompr_val = new_val;
185 rw_exit(&cmp->cmp_ompr_rwlock);
186 } else if (reg == T1394_CMP_IMPR) {
187 rw_enter(&cmp->cmp_impr_rwlock, RW_WRITER);
188 *old_valp = cmp->cmp_impr_val;
189 if (cmp->cmp_impr_val == arg_val) {
190 cmp->cmp_impr_val = new_val;
192 rw_exit(&cmp->cmp_impr_rwlock);
193 } else {
194 ret = DDI_FAILURE;
197 /* notify other targets */
198 if (ret == DDI_SUCCESS) {
199 s1394_cmp_notify_reg_change(hal, reg, target);
202 return (ret);
205 static void
206 s1394_cmp_init(s1394_hal_t *hal)
208 s1394_cmp_hal_t *cmp = &hal->hal_cmp;
210 rw_init(&cmp->cmp_ompr_rwlock, NULL, RW_DRIVER, NULL);
211 rw_init(&cmp->cmp_impr_rwlock, NULL, RW_DRIVER, NULL);
213 cmp->cmp_ompr_val = IEC61883_CMP_OMPR_INIT_VAL;
214 cmp->cmp_impr_val = IEC61883_CMP_IMPR_INIT_VAL;
217 static void
218 s1394_cmp_fini(s1394_hal_t *hal)
220 s1394_cmp_hal_t *cmp = &hal->hal_cmp;
222 rw_destroy(&cmp->cmp_ompr_rwlock);
223 rw_destroy(&cmp->cmp_impr_rwlock);
227 * iMPR/oMPR read/lock requests
229 static void
230 s1394_cmp_ompr_recv_read_request(cmd1394_cmd_t *req)
232 s1394_hal_t *hal = req->cmd_callback_arg;
233 s1394_cmp_hal_t *cmp = &hal->hal_cmp;
235 if (req->cmd_type != CMD1394_ASYNCH_RD_QUAD) {
236 req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
237 } else {
238 rw_enter(&cmp->cmp_ompr_rwlock, RW_READER);
239 req->cmd_u.q.quadlet_data = cmp->cmp_ompr_val;
240 rw_exit(&cmp->cmp_ompr_rwlock);
241 req->cmd_result = IEEE1394_RESP_COMPLETE;
244 (void) s1394_send_response(hal, req);
247 static void
248 s1394_cmp_impr_recv_read_request(cmd1394_cmd_t *req)
250 s1394_hal_t *hal = req->cmd_callback_arg;
251 s1394_cmp_hal_t *cmp = &hal->hal_cmp;
253 if (req->cmd_type != CMD1394_ASYNCH_RD_QUAD) {
254 req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
255 } else {
256 rw_enter(&cmp->cmp_impr_rwlock, RW_READER);
257 req->cmd_u.q.quadlet_data = cmp->cmp_impr_val;
258 rw_exit(&cmp->cmp_impr_rwlock);
259 req->cmd_result = IEEE1394_RESP_COMPLETE;
262 (void) s1394_send_response(hal, req);
265 static void
266 s1394_cmp_ompr_recv_lock_request(cmd1394_cmd_t *req)
268 s1394_hal_t *hal = req->cmd_callback_arg;
269 s1394_cmp_hal_t *cmp = &hal->hal_cmp;
270 boolean_t notify = B_TRUE;
272 if ((req->cmd_type != CMD1394_ASYNCH_LOCK_32) ||
273 (req->cmd_u.l32.lock_type != CMD1394_LOCK_COMPARE_SWAP)) {
274 req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
275 notify = B_FALSE;
276 } else {
277 rw_enter(&cmp->cmp_ompr_rwlock, RW_WRITER);
278 req->cmd_u.l32.old_value = cmp->cmp_ompr_val;
279 if (cmp->cmp_ompr_val == req->cmd_u.l32.arg_value) {
280 /* write only allowed bits */
281 cmp->cmp_ompr_val = (req->cmd_u.l32.data_value &
282 IEC61883_CMP_OMPR_LOCK_MASK) |
283 (cmp->cmp_ompr_val & ~IEC61883_CMP_OMPR_LOCK_MASK);
285 rw_exit(&cmp->cmp_ompr_rwlock);
286 req->cmd_result = IEEE1394_RESP_COMPLETE;
289 (void) s1394_send_response(hal, req);
291 /* notify all targets */
292 if (notify) {
293 s1394_cmp_notify_reg_change(hal, T1394_CMP_OMPR, NULL);
297 static void
298 s1394_cmp_impr_recv_lock_request(cmd1394_cmd_t *req)
300 s1394_hal_t *hal = req->cmd_callback_arg;
301 s1394_cmp_hal_t *cmp = &hal->hal_cmp;
302 boolean_t notify = B_TRUE;
304 if ((req->cmd_type != CMD1394_ASYNCH_LOCK_32) ||
305 (req->cmd_u.l32.lock_type != CMD1394_LOCK_COMPARE_SWAP)) {
306 req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
307 notify = B_FALSE;
308 } else {
309 rw_enter(&cmp->cmp_impr_rwlock, RW_WRITER);
310 req->cmd_u.l32.old_value = cmp->cmp_impr_val;
311 if (cmp->cmp_impr_val == req->cmd_u.l32.arg_value) {
312 /* write only allowed bits */
313 cmp->cmp_impr_val = (req->cmd_u.l32.data_value &
314 IEC61883_CMP_IMPR_LOCK_MASK) |
315 (cmp->cmp_impr_val & ~IEC61883_CMP_IMPR_LOCK_MASK);
317 rw_exit(&cmp->cmp_impr_rwlock);
318 req->cmd_result = IEEE1394_RESP_COMPLETE;
321 (void) s1394_send_response(hal, req);
323 /* notify all targets */
324 if (notify) {
325 s1394_cmp_notify_reg_change(hal, T1394_CMP_IMPR, NULL);
330 * Notify registered targets except 'self' about register value change
332 static void
333 s1394_cmp_notify_reg_change(s1394_hal_t *hal, t1394_cmp_reg_t reg,
334 s1394_target_t *self)
336 s1394_target_t *target;
337 s1394_fa_target_t *fat;
338 uint_t saved_gen;
339 int num_retries = 0;
340 void (*cb)(opaque_t, t1394_cmp_reg_t);
341 opaque_t arg;
343 rw_enter(&hal->target_list_rwlock, RW_READER);
345 start:
346 target = hal->hal_fa[S1394_FA_TYPE_CMP].fal_head;
348 for (; target; target = fat->fat_next) {
349 fat = &target->target_fa[S1394_FA_TYPE_CMP];
352 * even if the target list changes when the lock is dropped,
353 * comparing with self is safe because the target should
354 * not unregister until all CMP operations are completed
356 if (target == self) {
357 continue;
360 cb = fat->fat_u.cmp.cm_evts.cmp_reg_change;
361 if (cb == NULL) {
362 continue;
364 arg = fat->fat_u.cmp.cm_evts.cmp_arg;
366 saved_gen = s1394_fa_list_gen(hal, S1394_FA_TYPE_CMP);
368 rw_exit(&hal->target_list_rwlock);
369 cb(arg, reg);
370 rw_enter(&hal->target_list_rwlock, RW_READER);
373 * List could change while we dropped the lock. In such
374 * case, start all over again, because missing a register
375 * change can have more serious consequences for a
376 * target than receiving same notification more than once
378 if (saved_gen != s1394_fa_list_gen(hal, S1394_FA_TYPE_CMP)) {
379 if (++num_retries <= s1394_cmp_notify_retry_cnt) {
380 goto start;
381 } else {
382 break;
387 rw_exit(&hal->target_list_rwlock);