xref: /illumos-gate/usr/src/uts/common/io/1394/s1394_cmp.c (revision 8e458de0baeb1fee50643403223bc7e909a48464)
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