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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * av1394 CMP (Connection Management Procedures)
28  */
29 #include <sys/1394/targets/av1394/av1394_impl.h>
30 
31 /* configuration routines */
32 static void	av1394_cmp_cleanup(av1394_inst_t *icp);
33 
34 /* ioctl routines */
35 static int	av1394_ioctl_plug_init_local(av1394_inst_t *,
36 		iec61883_plug_init_t *);
37 static int	av1394_ioctl_plug_init_remote(av1394_inst_t *,
38 		iec61883_plug_init_t *);
39 
40 /* local PCR routines */
41 static int	av1394_pcr_init(av1394_inst_t *, int, uint32_t);
42 static void	av1394_pcr_fini(av1394_inst_t *, int);
43 static int	av1394_pcr_alloc_addr(av1394_inst_t *, uint64_t,
44 		t1394_addr_handle_t *);
45 static void	av1394_pcr_free_addr(av1394_inst_t *, t1394_addr_handle_t *);
46 static int	av1394_pcr_make_ph(int, int, int);
47 static int	av1394_pcr_ph2idx(int);
48 static av1394_pcr_t *av1394_pcr_ph2pcr(av1394_cmp_t *, int);
49 static uint64_t	av1394_pcr_idx2addr(int);
50 static int	av1394_pcr_idx2num(int);
51 static boolean_t av1394_pcr_idx_is_mpr(int);
52 static boolean_t av1394_pcr_ph_is_mpr(int);
53 static boolean_t av1394_pcr_ph_is_remote(int);
54 
55 /* callbacks */
56 static void	av1394_pcr_recv_read_request(cmd1394_cmd_t *);
57 static void	av1394_pcr_recv_lock_request(cmd1394_cmd_t *);
58 
59 /* remote PCR routines */
60 static int	av1394_pcr_remote_read(av1394_inst_t *, int, uint32_t *);
61 static int	av1394_pcr_remote_cas(av1394_inst_t *, int, uint32_t *,
62 		uint32_t, uint32_t);
63 
64 int
av1394_cmp_init(av1394_inst_t * avp)65 av1394_cmp_init(av1394_inst_t *avp)
66 {
67 	av1394_cmp_t	*cmp = &avp->av_i.i_cmp;
68 	ddi_iblock_cookie_t ibc = avp->av_attachinfo.iblock_cookie;
69 	int		ret;
70 
71 	ret = t1394_cmp_register(avp->av_t1394_hdl, NULL, 0);
72 
73 	if (ret == DDI_SUCCESS) {
74 		rw_init(&cmp->cmp_pcr_rwlock, NULL, RW_DRIVER, ibc);
75 	}
76 
77 	return (ret);
78 }
79 
80 void
av1394_cmp_fini(av1394_inst_t * avp)81 av1394_cmp_fini(av1394_inst_t *avp)
82 {
83 	av1394_cmp_cleanup(avp);
84 }
85 
86 void
av1394_cmp_bus_reset(av1394_inst_t * avp)87 av1394_cmp_bus_reset(av1394_inst_t *avp)
88 {
89 	av1394_cmp_t	*cmp = &avp->av_i.i_cmp;
90 	int		i;
91 
92 	/* reset PCR values */
93 	rw_enter(&cmp->cmp_pcr_rwlock, RW_WRITER);
94 	for (i = 0; i < NELEM(cmp->cmp_pcr); i++) {
95 		if ((i == AV1394_OMPR_IDX) || (i == AV1394_IMPR_IDX)) {
96 			continue;
97 		}
98 		if (cmp->cmp_pcr[i]) {
99 			if (i < AV1394_IMPR_IDX) {
100 				cmp->cmp_pcr[i]->pcr_val &=
101 				    ~AV1394_OPCR_BR_CLEAR_MASK;
102 			} else {
103 				cmp->cmp_pcr[i]->pcr_val &=
104 				    ~AV1394_IPCR_BR_CLEAR_MASK;
105 			}
106 		}
107 	}
108 	rw_exit(&cmp->cmp_pcr_rwlock);
109 }
110 
111 /*
112  * on close, free iPCRs and oPCRs not finalized by application
113  */
114 void
av1394_cmp_close(av1394_inst_t * avp)115 av1394_cmp_close(av1394_inst_t *avp)
116 {
117 	av1394_cmp_t	*cmp = &avp->av_i.i_cmp;
118 	int		i;
119 
120 	rw_enter(&cmp->cmp_pcr_rwlock, RW_WRITER);
121 	for (i = 0; i < NELEM(cmp->cmp_pcr); i++) {
122 		if ((i == AV1394_OMPR_IDX) || (i == AV1394_IMPR_IDX)) {
123 			continue;
124 		}
125 		if (cmp->cmp_pcr[i]) {
126 			av1394_pcr_fini(avp, i);
127 		}
128 	}
129 	rw_exit(&cmp->cmp_pcr_rwlock);
130 }
131 
132 /*
133  *
134  * --- ioctls
135  *
136  * IEC61883_PLUG_INIT
137  */
138 int
av1394_ioctl_plug_init(av1394_inst_t * avp,void * arg,int mode)139 av1394_ioctl_plug_init(av1394_inst_t *avp, void *arg, int mode)
140 {
141 	int		ret = 0;
142 	iec61883_plug_init_t pi;
143 
144 	if (ddi_copyin(arg, &pi, sizeof (pi), mode) != 0) {
145 		return (EFAULT);
146 	}
147 
148 	/* check arguments */
149 	if (((pi.pi_type != IEC61883_PLUG_IN) &&
150 	    (pi.pi_type != IEC61883_PLUG_OUT) &&
151 	    (pi.pi_type != IEC61883_PLUG_MASTER_IN) &&
152 	    (pi.pi_type != IEC61883_PLUG_MASTER_OUT)) ||
153 	    (((pi.pi_num < 0) || (pi.pi_num >= AV1394_NPCR)) &&
154 	    (pi.pi_num != IEC61883_PLUG_ANY))) {
155 		return (EINVAL);
156 	}
157 
158 	if (pi.pi_loc == IEC61883_LOC_LOCAL) {
159 		ret = av1394_ioctl_plug_init_local(avp, &pi);
160 	} else if (pi.pi_loc == IEC61883_LOC_REMOTE) {
161 		ret = av1394_ioctl_plug_init_remote(avp, &pi);
162 	} else {
163 		ret = EINVAL;
164 	}
165 
166 	if (ret == 0) {
167 		if (ddi_copyout(&pi, arg, sizeof (pi), mode) != 0) {
168 			ret = EFAULT;
169 		}
170 	}
171 
172 	return (ret);
173 }
174 
175 /*
176  * IEC61883_PLUG_FINI
177  */
178 /*ARGSUSED*/
179 int
av1394_ioctl_plug_fini(av1394_inst_t * avp,void * arg,int mode)180 av1394_ioctl_plug_fini(av1394_inst_t *avp, void *arg, int mode)
181 {
182 	av1394_cmp_t	*cmp = &avp->av_i.i_cmp;
183 	int		ret;
184 	int		ph;
185 
186 	ph = (int)(intptr_t)arg;
187 
188 	if (av1394_pcr_ph_is_remote(ph) || av1394_pcr_ph_is_mpr(ph)) {
189 		return (0);
190 	}
191 
192 	rw_enter(&cmp->cmp_pcr_rwlock, RW_WRITER);
193 	if (av1394_pcr_ph2pcr(cmp, ph) != NULL) {
194 		av1394_pcr_fini(avp, av1394_pcr_ph2idx(ph));
195 		ret = 0;
196 	} else {
197 		ret = EINVAL;
198 	}
199 	rw_exit(&cmp->cmp_pcr_rwlock);
200 
201 	return (ret);
202 }
203 
204 /*
205  * IEC61883_PLUG_REG_READ
206  */
207 int
av1394_ioctl_plug_reg_read(av1394_inst_t * avp,void * arg,int mode)208 av1394_ioctl_plug_reg_read(av1394_inst_t *avp, void *arg, int mode)
209 {
210 	av1394_cmp_t	*cmp = &avp->av_i.i_cmp;
211 	int		ret = 0;
212 	iec61883_plug_reg_val_t pr;
213 	int		ph;
214 	av1394_pcr_t	*pcr;
215 
216 	if (ddi_copyin(arg, &pr, sizeof (pr), mode) != 0) {
217 		return (EFAULT);
218 	}
219 	ph = pr.pr_handle;
220 
221 	if (av1394_pcr_ph_is_remote(ph)) {
222 		ret = av1394_pcr_remote_read(avp, ph, &pr.pr_val);
223 	} else {
224 		switch (av1394_pcr_ph2idx(ph)) {
225 		case AV1394_OMPR_IDX:
226 			ret = t1394_cmp_read(avp->av_t1394_hdl, T1394_CMP_OMPR,
227 			    &pr.pr_val);
228 			break;
229 		case AV1394_IMPR_IDX:
230 			ret = t1394_cmp_read(avp->av_t1394_hdl, T1394_CMP_IMPR,
231 			    &pr.pr_val);
232 			break;
233 		default:
234 			rw_enter(&cmp->cmp_pcr_rwlock, RW_READER);
235 			if ((pcr = av1394_pcr_ph2pcr(cmp, ph)) != NULL) {
236 				pr.pr_val = pcr->pcr_val;
237 			} else {
238 				ret = EINVAL;
239 			}
240 			rw_exit(&cmp->cmp_pcr_rwlock);
241 		}
242 	}
243 
244 	if (ret == 0) {
245 		if (ddi_copyout(&pr, arg, sizeof (pr), mode) != 0) {
246 			ret = EFAULT;
247 		}
248 	}
249 
250 	return (ret);
251 }
252 
253 /*
254  * IEC61883_PLUG_REG_CAS
255  */
256 int
av1394_ioctl_plug_reg_cas(av1394_inst_t * avp,void * arg,int mode)257 av1394_ioctl_plug_reg_cas(av1394_inst_t *avp, void *arg, int mode)
258 {
259 	av1394_cmp_t	*cmp = &avp->av_i.i_cmp;
260 	int		ret = 0;
261 	iec61883_plug_reg_lock_t pl;
262 	int		ph;
263 	av1394_pcr_t	*pcr;
264 
265 	if (ddi_copyin(arg, &pl, sizeof (pl), mode) != 0) {
266 		return (EFAULT);
267 	}
268 	ph = pl.pl_handle;
269 
270 	if (av1394_pcr_ph_is_remote(ph)) {
271 		ret = av1394_pcr_remote_cas(avp, ph,
272 		    &pl.pl_old, pl.pl_data, pl.pl_arg);
273 	} else {
274 		switch (av1394_pcr_ph2idx(ph)) {
275 		case AV1394_OMPR_IDX:
276 			ret = t1394_cmp_cas(avp->av_t1394_hdl, T1394_CMP_OMPR,
277 			    pl.pl_arg, pl.pl_data, &pl.pl_old);
278 			break;
279 		case AV1394_IMPR_IDX:
280 			ret = t1394_cmp_cas(avp->av_t1394_hdl, T1394_CMP_IMPR,
281 			    pl.pl_arg, pl.pl_data, &pl.pl_old);
282 			break;
283 		default:
284 			rw_enter(&cmp->cmp_pcr_rwlock, RW_WRITER);
285 			if ((pcr = av1394_pcr_ph2pcr(cmp, ph)) != NULL) {
286 				/* compare_swap */
287 				pl.pl_old = pcr->pcr_val;
288 				if (pcr->pcr_val == pl.pl_arg) {
289 					pcr->pcr_val = pl.pl_data;
290 				}
291 			} else {
292 				ret = EINVAL;
293 			}
294 			rw_exit(&cmp->cmp_pcr_rwlock);
295 		}
296 	}
297 
298 	if (ret == 0) {
299 		if (ddi_copyout(&pl, arg, sizeof (pl), mode) != 0) {
300 			ret = EFAULT;
301 		}
302 	}
303 
304 	return (ret);
305 }
306 
307 
308 /*
309  *
310  * --- configuration routines
311  *
312  */
313 static void
av1394_cmp_cleanup(av1394_inst_t * avp)314 av1394_cmp_cleanup(av1394_inst_t *avp)
315 {
316 	av1394_cmp_t	*cmp = &avp->av_i.i_cmp;
317 	int		i;
318 
319 	rw_enter(&cmp->cmp_pcr_rwlock, RW_WRITER);
320 	for (i = 0; i < NELEM(cmp->cmp_pcr); i++) {
321 		if (cmp->cmp_pcr[i]) {
322 			av1394_pcr_fini(avp, i);
323 		}
324 	}
325 	rw_exit(&cmp->cmp_pcr_rwlock);
326 	rw_destroy(&cmp->cmp_pcr_rwlock);
327 	(void) t1394_cmp_unregister(avp->av_t1394_hdl);
328 }
329 
330 
331 /*
332  *
333  * --- ioctl routines
334  *
335  * IEC61883_PLUG_INIT for local plugs
336  */
337 static int
av1394_ioctl_plug_init_local(av1394_inst_t * avp,iec61883_plug_init_t * pip)338 av1394_ioctl_plug_init_local(av1394_inst_t *avp, iec61883_plug_init_t *pip)
339 {
340 	av1394_cmp_t	*cmp = &avp->av_i.i_cmp;
341 	int		err;
342 	int		ph;		/* plug handle */
343 	int		idx, max_idx;	/* plug index */
344 
345 	/* MPR's are a special case */
346 	if ((pip->pi_type == IEC61883_PLUG_MASTER_IN) ||
347 	    (pip->pi_type == IEC61883_PLUG_MASTER_OUT)) {
348 		pip->pi_handle = av1394_pcr_make_ph(pip->pi_loc,
349 		    pip->pi_type, 0);
350 		return (0);
351 	}
352 
353 	/* PCR */
354 	rw_enter(&cmp->cmp_pcr_rwlock, RW_WRITER);
355 	if (pip->pi_num == IEC61883_PLUG_ANY) {
356 		if (pip->pi_type == IEC61883_PLUG_OUT) {
357 			idx = AV1394_OPCR0_IDX;
358 			max_idx = idx + AV1394_PCR_ADDR_NOPCR - 1;
359 		} else {
360 			ASSERT(pip->pi_type == IEC61883_PLUG_IN);
361 			idx = AV1394_IPCR0_IDX;
362 			max_idx = idx + AV1394_PCR_ADDR_NIPCR - 1;
363 		}
364 
365 		/* find unused PCR */
366 		for (; idx <= max_idx; idx++) {
367 			if (cmp->cmp_pcr[idx] != NULL) {
368 				continue;
369 			}
370 			err = av1394_pcr_init(avp, idx, AV1394_PCR_INIT_VAL);
371 			if (err == DDI_SUCCESS) {
372 				break;
373 			}
374 		}
375 	} else {
376 		ph = av1394_pcr_make_ph(pip->pi_loc, pip->pi_type, pip->pi_num);
377 		idx = max_idx = av1394_pcr_ph2idx(ph);
378 
379 		/* create PCR if not already */
380 		if (cmp->cmp_pcr[idx] == NULL) {
381 			err = av1394_pcr_init(avp, idx, AV1394_PCR_INIT_VAL);
382 		}
383 	}
384 
385 	rw_exit(&cmp->cmp_pcr_rwlock);
386 
387 	if ((err != DDI_SUCCESS) || (idx > max_idx)) {
388 		return (EBUSY);
389 	}
390 	pip->pi_rnum = av1394_pcr_idx2num(idx);
391 	pip->pi_handle = av1394_pcr_make_ph(pip->pi_loc, pip->pi_type,
392 	    pip->pi_rnum);
393 
394 	return (0);
395 }
396 
397 /*
398  * IEC61883_PLUG_INIT for remote plugs
399  */
400 static int
av1394_ioctl_plug_init_remote(av1394_inst_t * avp,iec61883_plug_init_t * pip)401 av1394_ioctl_plug_init_remote(av1394_inst_t *avp, iec61883_plug_init_t *pip)
402 {
403 	int		ph;
404 	uint32_t	val;
405 	int		ret;
406 
407 	if (pip->pi_num == IEC61883_PLUG_ANY) {
408 		return (EINVAL);
409 	}
410 
411 	ph = av1394_pcr_make_ph(pip->pi_loc, pip->pi_type, pip->pi_num);
412 
413 	/* check PCR existance by attempting to read it */
414 	if ((ret = av1394_pcr_remote_read(avp, ph, &val)) == 0) {
415 		pip->pi_handle = ph;
416 		pip->pi_rnum = pip->pi_num;
417 	}
418 
419 	return (ret);
420 }
421 
422 
423 /*
424  *
425  * --- plug routines
426  *
427  * initialize a PCR
428  */
429 static int
av1394_pcr_init(av1394_inst_t * avp,int idx,uint32_t val)430 av1394_pcr_init(av1394_inst_t *avp, int idx, uint32_t val)
431 {
432 	av1394_cmp_t	*cmp = &avp->av_i.i_cmp;
433 	av1394_pcr_t	*pcr;
434 	uint64_t	addr;
435 	int		ret;
436 
437 	pcr = kmem_zalloc(sizeof (av1394_pcr_t), KM_SLEEP);
438 	pcr->pcr_val = val;
439 	cmp->cmp_pcr[idx] = pcr;
440 
441 	addr = av1394_pcr_idx2addr(idx);
442 	ret = av1394_pcr_alloc_addr(avp, addr, &pcr->pcr_addr_hdl);
443 	if (ret != DDI_SUCCESS) {
444 		kmem_free(pcr, sizeof (av1394_pcr_t));
445 		cmp->cmp_pcr[idx] = NULL;
446 	}
447 
448 	return (ret);
449 }
450 
451 /*
452  * finalize a PCR
453  */
454 static void
av1394_pcr_fini(av1394_inst_t * avp,int idx)455 av1394_pcr_fini(av1394_inst_t *avp, int idx)
456 {
457 	av1394_cmp_t	*cmp = &avp->av_i.i_cmp;
458 
459 	av1394_pcr_free_addr(avp, &cmp->cmp_pcr[idx]->pcr_addr_hdl);
460 	kmem_free(cmp->cmp_pcr[idx], sizeof (av1394_pcr_t));
461 	cmp->cmp_pcr[idx] = NULL;
462 }
463 
464 /*
465  * allocate CSR address for a PCR
466  */
467 static int
av1394_pcr_alloc_addr(av1394_inst_t * avp,uint64_t addr,t1394_addr_handle_t * hdlp)468 av1394_pcr_alloc_addr(av1394_inst_t *avp, uint64_t addr,
469 		t1394_addr_handle_t *hdlp)
470 {
471 	t1394_alloc_addr_t aa;
472 	int		ret;
473 	int		result;
474 
475 	bzero(&aa, sizeof (aa));
476 	aa.aa_address = addr;
477 	aa.aa_length = 4;
478 	aa.aa_type = T1394_ADDR_FIXED;
479 	aa.aa_enable = T1394_ADDR_RDENBL | T1394_ADDR_LKENBL;
480 	aa.aa_evts.recv_read_request = av1394_pcr_recv_read_request;
481 	aa.aa_evts.recv_lock_request = av1394_pcr_recv_lock_request;
482 	aa.aa_arg = avp;
483 
484 	ret = t1394_alloc_addr(avp->av_t1394_hdl, &aa, 0, &result);
485 	if (ret == DDI_SUCCESS) {
486 		*hdlp = aa.aa_hdl;
487 	}
488 
489 	return (ret);
490 }
491 
492 /*
493  * free CSR address occupied by a PCR
494  */
495 static void
av1394_pcr_free_addr(av1394_inst_t * avp,t1394_addr_handle_t * hdlp)496 av1394_pcr_free_addr(av1394_inst_t *avp, t1394_addr_handle_t *hdlp)
497 {
498 	(void) t1394_free_addr(avp->av_t1394_hdl, hdlp, 0);
499 }
500 
501 /*
502  * make plug handle. range checking should be performed by caller
503  */
504 static int
av1394_pcr_make_ph(int loc,int type,int num)505 av1394_pcr_make_ph(int loc, int type, int num)
506 {
507 	int	ph;
508 
509 	switch (type) {
510 	case IEC61883_PLUG_IN:
511 		ph = num + AV1394_IPCR0_IDX;
512 		break;
513 	case IEC61883_PLUG_OUT:
514 		ph = num + AV1394_OPCR0_IDX;
515 		break;
516 	case IEC61883_PLUG_MASTER_IN:
517 		ph = AV1394_IMPR_IDX;
518 		break;
519 	case IEC61883_PLUG_MASTER_OUT:
520 		ph = AV1394_OMPR_IDX;
521 		break;
522 	default:
523 		ASSERT(0);
524 	}
525 
526 	if (loc == IEC61883_LOC_REMOTE) {
527 		ph |= AV1394_PCR_REMOTE;
528 	}
529 
530 	return (ph);
531 }
532 
533 /*
534  * convert plug handle to PCR index
535  */
536 static int
av1394_pcr_ph2idx(int ph)537 av1394_pcr_ph2idx(int ph)
538 {
539 	return (ph & ~AV1394_PCR_REMOTE);
540 }
541 
542 /*
543  * convert plug handle to PCR pointer
544  */
545 static av1394_pcr_t *
av1394_pcr_ph2pcr(av1394_cmp_t * cmp,int ph)546 av1394_pcr_ph2pcr(av1394_cmp_t *cmp, int ph)
547 {
548 	int	idx = av1394_pcr_ph2idx(ph);
549 
550 	if ((idx >= 0) && (idx < NELEM(cmp->cmp_pcr))) {
551 		return (cmp->cmp_pcr[idx]);
552 	} else {
553 		return (NULL);
554 	}
555 }
556 
557 /*
558  * convert PCR index to CSR address
559  */
560 static uint64_t
av1394_pcr_idx2addr(int idx)561 av1394_pcr_idx2addr(int idx)
562 {
563 	return (AV1394_PCR_ADDR_START + idx * 4);
564 }
565 
566 /*
567  * convert PCR index to number
568  */
569 static int
av1394_pcr_idx2num(int idx)570 av1394_pcr_idx2num(int idx)
571 {
572 	ASSERT(!av1394_pcr_idx_is_mpr(idx));
573 
574 	return ((idx - 1) % 32);
575 }
576 
577 /*
578  * returns B_TRUE if a master plug
579  */
580 static boolean_t
av1394_pcr_idx_is_mpr(int idx)581 av1394_pcr_idx_is_mpr(int idx)
582 {
583 	return (idx % 32 == 0);
584 }
585 
586 static boolean_t
av1394_pcr_ph_is_mpr(int ph)587 av1394_pcr_ph_is_mpr(int ph)
588 {
589 	return (av1394_pcr_ph2idx(ph) % 32 == 0);
590 }
591 
592 /*
593  * returns B_TRUE if a remote plug
594  */
595 static boolean_t
av1394_pcr_ph_is_remote(int ph)596 av1394_pcr_ph_is_remote(int ph)
597 {
598 	return ((ph & AV1394_PCR_REMOTE) != 0);
599 }
600 
601 
602 /*
603  *
604  * --- callbacks
605  *
606  */
607 static void
av1394_pcr_recv_read_request(cmd1394_cmd_t * req)608 av1394_pcr_recv_read_request(cmd1394_cmd_t *req)
609 {
610 	av1394_inst_t	*avp = req->cmd_callback_arg;
611 	av1394_cmp_t	*cmp = &avp->av_i.i_cmp;
612 	int		idx;	/* PCR index */
613 	av1394_pcr_t	*pcr;
614 
615 	idx = (req->cmd_addr - AV1394_PCR_ADDR_START) / 4;
616 
617 	if (req->cmd_type != CMD1394_ASYNCH_RD_QUAD) {
618 		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
619 	} else if ((idx >= NELEM(cmp->cmp_pcr)) ||
620 	    ((pcr = cmp->cmp_pcr[idx]) == NULL)) {
621 		req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR;
622 	} else {
623 		/* read */
624 		rw_enter(&cmp->cmp_pcr_rwlock, RW_READER);
625 		req->cmd_u.q.quadlet_data = pcr->pcr_val;
626 		rw_exit(&cmp->cmp_pcr_rwlock);
627 
628 		req->cmd_result = IEEE1394_RESP_COMPLETE;
629 	}
630 
631 	(void) t1394_recv_request_done(avp->av_t1394_hdl, req, 0);
632 }
633 
634 static void
av1394_pcr_recv_lock_request(cmd1394_cmd_t * req)635 av1394_pcr_recv_lock_request(cmd1394_cmd_t *req)
636 {
637 	av1394_inst_t	*avp = req->cmd_callback_arg;
638 	av1394_cmp_t	*cmp = &avp->av_i.i_cmp;
639 	int		idx;	/* PCR index */
640 	av1394_pcr_t	*pcr;
641 
642 	idx = (req->cmd_addr - AV1394_PCR_ADDR_START) / 4;
643 
644 	if ((req->cmd_type != CMD1394_ASYNCH_LOCK_32) ||
645 	    (req->cmd_u.l32.lock_type != CMD1394_LOCK_COMPARE_SWAP)) {
646 		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
647 	} else if ((idx >= NELEM(cmp->cmp_pcr)) ||
648 	    ((pcr = cmp->cmp_pcr[idx]) == NULL)) {
649 		req->cmd_result = IEEE1394_RESP_ADDRESS_ERROR;
650 	} else {
651 		/* compare_swap */
652 		rw_enter(&cmp->cmp_pcr_rwlock, RW_WRITER);
653 		if (pcr->pcr_val == req->cmd_u.l32.arg_value) {
654 			pcr->pcr_val = req->cmd_u.l32.data_value;
655 		}
656 		req->cmd_u.l32.old_value = pcr->pcr_val;
657 		rw_exit(&cmp->cmp_pcr_rwlock);
658 
659 		req->cmd_result = IEEE1394_RESP_COMPLETE;
660 	}
661 
662 	(void) t1394_recv_request_done(avp->av_t1394_hdl, req, 0);
663 }
664 
665 
666 /*
667  *
668  * --- remote PCR routines
669  *
670  * read specified PCR on the remote node
671  */
672 static int
av1394_pcr_remote_read(av1394_inst_t * avp,int ph,uint32_t * valp)673 av1394_pcr_remote_read(av1394_inst_t *avp, int ph, uint32_t *valp)
674 {
675 	cmd1394_cmd_t	*cmd;
676 	int		ret = 0;
677 	int		err;
678 
679 	ret = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd);
680 	if (ret != DDI_SUCCESS) {
681 		return (ENOMEM);
682 	}
683 
684 	cmd->cmd_addr = av1394_pcr_idx2addr(av1394_pcr_ph2idx(ph));
685 	cmd->cmd_type = CMD1394_ASYNCH_RD_QUAD;
686 	cmd->cmd_options = CMD1394_BLOCKING;
687 
688 	if (((err = t1394_read(avp->av_t1394_hdl, cmd)) == DDI_SUCCESS) &&
689 	    (cmd->cmd_result == CMD1394_CMDSUCCESS)) {
690 		*valp = cmd->cmd_u.q.quadlet_data;
691 	} else {
692 		ret = EIO;
693 	}
694 
695 	err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd);
696 	ASSERT(err == DDI_SUCCESS);
697 
698 	return (ret);
699 }
700 
701 /*
702  * compare_swap specified PCR on the remote node
703  */
704 static int
av1394_pcr_remote_cas(av1394_inst_t * avp,int ph,uint32_t * old_valuep,uint32_t data_value,uint32_t arg_value)705 av1394_pcr_remote_cas(av1394_inst_t *avp, int ph, uint32_t *old_valuep,
706 		uint32_t data_value, uint32_t arg_value)
707 {
708 	cmd1394_cmd_t	*cmd;
709 	int		ret = 0;
710 	int		err;
711 
712 	ret = t1394_alloc_cmd(avp->av_t1394_hdl, 0, &cmd);
713 	if (ret != DDI_SUCCESS) {
714 		return (ENOMEM);
715 	}
716 
717 	cmd->cmd_addr = av1394_pcr_idx2addr(av1394_pcr_ph2idx(ph));
718 	cmd->cmd_type = CMD1394_ASYNCH_LOCK_32;
719 	cmd->cmd_u.l32.lock_type = CMD1394_LOCK_COMPARE_SWAP;
720 	cmd->cmd_u.l32.data_value = data_value;
721 	cmd->cmd_u.l32.arg_value = arg_value;
722 	cmd->cmd_u.l32.num_retries = 0;
723 	cmd->cmd_options = CMD1394_BLOCKING;
724 
725 	if (((err = t1394_lock(avp->av_t1394_hdl, cmd)) == DDI_SUCCESS) &&
726 	    (cmd->cmd_result == CMD1394_CMDSUCCESS)) {
727 		*old_valuep = cmd->cmd_u.l32.old_value;
728 	} else {
729 		ret = EIO;
730 	}
731 
732 	err = t1394_free_cmd(avp->av_t1394_hdl, 0, &cmd);
733 	ASSERT(err == DDI_SUCCESS);
734 
735 	return (ret);
736 }
737