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