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