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
s1394_cmp_register(s1394_target_t * target,t1394_cmp_evts_t * evts)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
s1394_cmp_unregister(s1394_target_t * target)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
s1394_cmp_read(s1394_target_t * target,t1394_cmp_reg_t reg,uint32_t * valp)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
s1394_cmp_cas(s1394_target_t * target,t1394_cmp_reg_t reg,uint32_t arg_val,uint32_t new_val,uint32_t * old_valp)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
s1394_cmp_init(s1394_hal_t * hal)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
s1394_cmp_fini(s1394_hal_t * hal)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
s1394_cmp_ompr_recv_read_request(cmd1394_cmd_t * req)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
s1394_cmp_impr_recv_read_request(cmd1394_cmd_t * req)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
s1394_cmp_ompr_recv_lock_request(cmd1394_cmd_t * req)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
s1394_cmp_impr_recv_lock_request(cmd1394_cmd_t * req)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
s1394_cmp_notify_reg_change(s1394_hal_t * hal,t1394_cmp_reg_t reg,s1394_target_t * self)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