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