summaryrefslogtreecommitdiff
path: root/usr/src/uts/sun4u/starcat/io/cvc.c
blob: a6fe74807e83c28d59ae736082438015d9238d24 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


/*
 * MT STREAMS Virtual Console Device Driver
 */

#include <sys/types.h>
#include <sys/open.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/signal.h>
#include <sys/cred.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/disp.h>
#include <sys/vnode.h>
#include <sys/uio.h>
#include <sys/buf.h>
#include <sys/file.h>
#include <sys/kmem.h>
#include <sys/stat.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/strsubr.h>
#include <sys/strsun.h>
#include <sys/tty.h>
#include <sys/ptyvar.h>
#include <sys/poll.h>
#include <sys/debug.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/errno.h>
#include <sys/modctl.h>

#include <sys/sc_cvc.h>
#include <sys/sc_cvcio.h>
#include <sys/iosramio.h>

static int	cvc_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int	cvc_attach(dev_info_t *, ddi_attach_cmd_t);
static int	cvc_detach(dev_info_t *, ddi_detach_cmd_t);
static int	cvc_open(register queue_t *, dev_t *, int, int, cred_t *);
static int	cvc_close(queue_t *, int, cred_t *);
static int	cvc_wput(queue_t *, mblk_t *);
static int	cvc_wsrv(queue_t *);
static void	cvc_ioctl(queue_t *, mblk_t *);
static void	cvc_reioctl(void *);
static void	cvc_input_daemon(void);
static void	cvc_send_to_iosram(mblk_t **chainpp);
static void	cvc_flush_queue(void *);
static void	cvc_iosram_ops(uint8_t);
static void	cvc_getstr(char *cp);
static void	cvc_win_resize(int clear_flag);

#define	ESUCCESS 0
#ifndef	TRUE
#define	TRUE	1
#define	FALSE	0
#endif

/*
 * Private copy of devinfo pointer; cvc_info uses it.
 */
static dev_info_t	*cvcdip;

/*
 * This structure reflects the layout of data in CONI and CONO.  If you are
 * going to add fields that don't get written into those chunks, be sure to
 * place them _after_ the buffer field.
 */
typedef struct cvc_buf {
	ushort_t	count;
	uchar_t		buffer[MAX_XFER_COUTPUT];
} cvc_buf_t;

typedef struct cvc_s {
	bufcall_id_t	cvc_wbufcid;
	tty_common_t	cvc_tty;
} cvc_t;

cvc_t	cvc_common_tty;

static struct module_info cvcm_info = {
	1313,		/* mi_idnum Bad luck number  ;-) */
	"cvc",		/* mi_idname */
	0,		/* mi_minpsz */
	INFPSZ,		/* mi_maxpsz */
	2048,		/* mi_hiwat */
	2048		/* mi_lowat */
};

static struct qinit cvcrinit = {
	NULL,		/* qi_putp */
	NULL,		/* qi_srvp */
	cvc_open,	/* qi_qopen */
	cvc_close,	/* qi_qclose */
	NULL,		/* qi_qadmin */
	&cvcm_info,	/* qi_minfo */
	NULL		/* qi_mstat */
};

static struct qinit cvcwinit = {
	cvc_wput,	/* qi_putp */
	cvc_wsrv,	/* qi_srvp */
	cvc_open,	/* qi_qopen */
	cvc_close,	/* qi_qclose */
	NULL,		/* qi_qadmin */
	&cvcm_info,	/* qi_minfo */
	NULL		/* qi_mstat */
};

struct streamtab	cvcinfo = {
	&cvcrinit,	/* st_rdinit */
	&cvcwinit,	/* st_wrinit */
	NULL,		/* st_muxrinit */
	NULL		/* st_muxwrinit */
};

static krwlock_t	cvclock;	/* lock protecting everything here */
static queue_t		*cvcinput_q;	/* queue for console input */
static queue_t		*cvcoutput_q;	/* queue for console output */
static int		cvc_instance = -1;
static int		cvc_stopped = 0;
static int		cvc_suspended = 0;

kthread_id_t		cvc_input_daemon_thread; /* just to aid debugging */
static kmutex_t		cvcmutex;	/* protects input */
static kmutex_t		cvc_iosram_input_mutex; /* protects IOSRAM inp buff */
static int		input_ok = 0;	/* true when stream is valid */

static int		via_iosram = 0; /* toggle switch */
static timeout_id_t	cvc_timeout_id = (timeout_id_t)-1;
static int		input_daemon_started = 0;

/* debugging functions */
#ifdef DEBUG
uint32_t cvc_dbg_flags = 0x0;
static void cvc_dbg(uint32_t flag, char *fmt,
	uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5);
#endif

/*
 * Module linkage information for the kernel.
 */

DDI_DEFINE_STREAM_OPS(cvcops, nulldev, nulldev, cvc_attach, cvc_detach,
		    nodev, cvc_info, (D_NEW|D_MTPERQ|D_MP), &cvcinfo,
		    ddi_quiesce_not_supported);

extern int nodev(), nulldev();
extern struct mod_ops mod_driverops;

static struct modldrv modldrv = {
	&mod_driverops, /* Type of module.  This one is a pseudo driver */
	"CVC driver 'cvc'",
	&cvcops,	/* driver ops */
};

static struct modlinkage modlinkage = {
	MODREV_1,
	&modldrv,
	NULL
};

int
_init()
{
	int	status;

	status = mod_install(&modlinkage);
	if (status == 0) {
		mutex_init(&cvcmutex, NULL, MUTEX_DEFAULT, NULL);
	}
	return (status);
}

int
_fini()
{
	return (EBUSY);
}

int
_info(struct modinfo *modinfop)
{
	return (mod_info(&modlinkage, modinfop));
}

/*
 * DDI glue routines.
 */

/* ARGSUSED */
static int
cvc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
	static char	been_here = 0;

	if (cmd == DDI_RESUME) {
		cvc_suspended = 0;
		if (cvcinput_q != NULL) {
			qenable(WR(cvcinput_q));
		}
		return (DDI_SUCCESS);
	}

	mutex_enter(&cvcmutex);
	if (!been_here) {
		been_here = 1;
		mutex_init(&cvc_iosram_input_mutex, NULL, MUTEX_DEFAULT, NULL);
		rw_init(&cvclock, NULL, RW_DRIVER, NULL);
		cvc_instance = ddi_get_instance(devi);
	} else {
#if defined(DEBUG)
		cmn_err(CE_NOTE,
		    "cvc_attach: called multiple times!! (instance = %d)",
		    ddi_get_instance(devi));
#endif /* DEBUG */
		mutex_exit(&cvcmutex);
		return (DDI_SUCCESS);
	}
	mutex_exit(&cvcmutex);

	if (ddi_create_minor_node(devi, "cvc", S_IFCHR,
	    0, NULL, NULL) == DDI_FAILURE) {
		ddi_remove_minor_node(devi, NULL);
		return (-1);
	}
	cvcdip = devi;
	cvcinput_q = NULL;
	cvcoutput_q = NULL;

	CVC_DBG0(CVC_DBG_ATTACH, "Attached");

	return (DDI_SUCCESS);
}

static int
cvc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
	if (cmd == DDI_SUSPEND) {
		cvc_suspended = 1;
	} else {
		if (cmd != DDI_DETACH) {
			return (DDI_FAILURE);
		}
		/*
		 * XXX this doesn't even begin to address the detach
		 * issues - it doesn't terminate the outstanding thread,
		 * it doesn't clean up mutexes, kill the timeout routine
		 * etc.
		 */
		if (cvc_instance == ddi_get_instance(dip)) {
			ddi_remove_minor_node(dip, NULL);
		}
	}

	CVC_DBG0(CVC_DBG_DETACH, "Detached");

	return (DDI_SUCCESS);
}

/* ARGSUSED */
static int
cvc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
	register int error;

	switch (infocmd) {
	case DDI_INFO_DEVT2DEVINFO:
		if (cvcdip == NULL) {
			error = DDI_FAILURE;
		} else {
			*result = (void *)cvcdip;
			error = DDI_SUCCESS;
		}
		break;
	case DDI_INFO_DEVT2INSTANCE:
		*result = (void *)0;
		error = DDI_SUCCESS;
		break;
	default:
		error = DDI_FAILURE;
	}
	return (error);
}

/* ARGSUSED */
static int
cvc_open(register queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
{
	register int		unit = getminor(*devp);
	register int		err = DDI_SUCCESS;
	tty_common_t		*tty;
	cvc_t			*cp;

	if (unit != 0)
		return (ENXIO);

	if (q->q_ptr)
		return (0);

	cp = (cvc_t *)&cvc_common_tty;
	bzero((caddr_t)cp, sizeof (cvc_t));
	cp->cvc_wbufcid = 0;
	tty = &cp->cvc_tty;
	tty->t_readq = q;
	tty->t_writeq = WR(q);
	WR(q)->q_ptr = q->q_ptr = (caddr_t)cp;
	cvcinput_q = RD(q);		/* save for cvc_redir */
	qprocson(q);
	mutex_enter(&cvcmutex);
	input_ok = 1;

	/*
	 * Start the thread that handles input polling if it hasn't been started
	 * previously.
	 */
	if (!input_daemon_started) {
		input_daemon_started = 1;
		mutex_exit(&cvcmutex);

		cvc_input_daemon_thread = thread_create(NULL, 0,
		    cvc_input_daemon, NULL, 0, &p0, TS_RUN, minclsyspri);
		CVC_DBG0(CVC_DBG_IOSRAM_RD, "Started input daemon");
	} else {
		mutex_exit(&cvcmutex);
	}

	/*
	 * Set the console window size.
	 */
	mutex_enter(&cvc_iosram_input_mutex);
	cvc_win_resize(FALSE);
	mutex_exit(&cvc_iosram_input_mutex);

	CVC_DBG0(CVC_DBG_OPEN, "Plumbed successfully");

	return (err);
}

/* ARGSUSED */
static int
cvc_close(queue_t *q, int flag, cred_t *crp)
{
	register int		err = DDI_SUCCESS;
	register cvc_t		*cp;

	mutex_enter(&cvcmutex);
	input_ok = 0;
	mutex_exit(&cvcmutex);

	cp = q->q_ptr;
	if (cp->cvc_wbufcid != 0) {
		unbufcall(cp->cvc_wbufcid);
	}
	ttycommon_close(&cp->cvc_tty);
	WR(q)->q_ptr = q->q_ptr = NULL;
	cvcinput_q = NULL;
	bzero((caddr_t)cp, sizeof (cvc_t));
	qprocsoff(q);

	CVC_DBG0(CVC_DBG_CLOSE, "Un-plumbed successfully");

	return (err);
}


/*
 * cvc_wput()
 *	cn driver does a strwrite of console output data to rconsvp which has
 *	been set by consconfig. The data enters the cvc stream at the streamhead
 *	and flows thru ttycompat and ldterm which have been pushed on the
 *	stream.  Console output data gets sent out either to cvcredir, if the
 *	network path is available and selected, or to IOSRAM otherwise.  Data is
 *	sent to cvcredir via its read queue (cvcoutput_q, which gets set in
 *	cvc_register()).  If the IOSRAM path is selected, or if previous mblks
 *	are currently queued up for processing, the new mblk will be queued
 *	and handled later on by cvc_wsrv.
 */
static int
cvc_wput(queue_t *q, mblk_t *mp)
{
	int		error = 0;

	rw_enter(&cvclock, RW_READER);

	CVC_DBG2(CVC_DBG_WPUT, "mp 0x%x db_type 0x%x",
	    mp, mp->b_datap->db_type);

	switch (mp->b_datap->db_type) {

		case M_IOCTL:
		case M_CTL: {
			struct iocblk *iocp = (struct iocblk *)mp->b_rptr;

			switch (iocp->ioc_cmd) {
				/*
				 * These ioctls are only supposed to be
				 * processed after everything else that is
				 * already queued awaiting processing, so throw
				 * them on the queue and let cvc_wsrv handle
				 * them.
				 */
				case TCSETSW:
				case TCSETSF:
				case TCSETAW:
				case TCSETAF:
				case TCSBRK:
					(void) putq(q, mp);
					break;

				default:
					cvc_ioctl(q, mp);
			}
			break;
		}

		case M_FLUSH:
			if (*mp->b_rptr & FLUSHW) {
				/*
				 * Flush our write queue.
				 */
				flushq(q, FLUSHDATA);
				*mp->b_rptr &= ~FLUSHW;
			}
			if (*mp->b_rptr & FLUSHR) {
				flushq(RD(q), FLUSHDATA);
				qreply(q, mp);
			} else
				freemsg(mp);
			break;

		case M_STOP:
			cvc_stopped = 1;
			freemsg(mp);
			break;

		case M_START:
			cvc_stopped = 0;
			freemsg(mp);
			qenable(q);  /* Start up delayed messages */
			break;

		case M_READ:
			/*
			 * ldterm handles this (VMIN/VTIME processing).
			 */
			freemsg(mp);
			break;

		default:
			cmn_err(CE_WARN, "cvc_wput: unexpected mblk type - mp ="
			    " 0x%p, type = 0x%x", (void *)mp,
			    mp->b_datap->db_type);
			freemsg(mp);
			break;

		case M_DATA:
			/*
			 * If there are other mblks queued up for transmission,
			 * or we're using IOSRAM either because cvcredir hasn't
			 * registered yet or because we were configured that
			 * way, or cvc has been stopped or suspended, place this
			 * mblk on the input queue for future processing.
			 * Otherwise, hand it off to cvcredir for transmission
			 * via the network.
			 */
			if (q->q_first != NULL || cvcoutput_q == NULL ||
			    via_iosram || cvc_stopped == 1 ||
			    cvc_suspended == 1) {
				(void) putq(q, mp);
			} else {
				/*
				 * XXX - should canputnext be called here?
				 * Starfire's cvc doesn't do that, and it
				 * appears to work anyway.
				 */
				(void) putnext(cvcoutput_q, mp);
			}
			break;

	}
	rw_exit(&cvclock);
	return (error);
}

/*
 * cvc_wsrv()
 *	cvc_wsrv handles mblks that have been queued by cvc_wput either because
 *	the IOSRAM path was selected or the queue contained preceding mblks.  To
 *	optimize processing (particularly if the IOSRAM path is selected), all
 *	mblks are pulled off of the queue and chained together.  Then, if there
 *	are any mblks on the chain, they are either forwarded to cvcredir or
 *	sent for IOSRAM processing as appropriate given current circumstances.
 *	IOSRAM processing may not be able to handle all of the data in the
 *	chain, in which case the remaining data is placed back on the queue and
 *	a timeout routine is registered to reschedule cvc_wsrv in the future.
 *	Automatic scheduling of the queue is disabled (noenable(q)) while
 *	cvc_wsrv is running to avoid superfluous calls.
 */
static int
cvc_wsrv(queue_t *q)
{
	mblk_t *total_mp = NULL;
	mblk_t *mp;

	if (cvc_stopped == 1 || cvc_suspended == 1) {
		return (0);
	}

	rw_enter(&cvclock, RW_READER);
	noenable(q);

	/*
	 * If there's already a timeout registered for scheduling this routine
	 * in the future, it's a safe bet that we don't want to run right now.
	 */
	if (cvc_timeout_id != (timeout_id_t)-1) {
		enableok(q);
		rw_exit(&cvclock);
		return (0);
	}

	/*
	 * Start by linking all of the queued M_DATA mblks into a single chain
	 * so we can flush as much as possible to IOSRAM (if we choose that
	 * route).
	 */
	while ((mp = getq(q)) != NULL) {
		/*
		 * Technically, certain IOCTLs are supposed to be processed only
		 * after all preceding data has completely "drained".  In an
		 * attempt to support that, we delay processing of those IOCTLs
		 * until this point.  It is still possible that an IOCTL will be
		 * processed before all preceding data is drained, for instance
		 * in the case where not all of the preceding data would fit
		 * into IOSRAM and we have to place it back on the queue.
		 * However, since none of these IOCTLs really appear to have any
		 * relevance for cvc, and we weren't supporting delayed
		 * processing at _all_ previously, this partial implementation
		 * should suffice.  (Fully implementing the delayed IOCTL
		 * processing would be unjustifiably difficult given the nature
		 * of the underlying IOSRAM console protocol.)
		 */
		if (mp->b_datap->db_type == M_IOCTL) {
			cvc_ioctl(q, mp);
			continue;
		}

		/*
		 * We know that only M_IOCTL and M_DATA blocks are placed on our
		 * queue.  Since this block isn't an M_IOCTL, it must be M_DATA.
		 */
		if (total_mp != NULL) {
			linkb(total_mp, mp);
		} else {
			total_mp = mp;
		}
	}

	/*
	 * Do we actually have anything to do?
	 */
	if (total_mp == NULL) {
		enableok(q);
		rw_exit(&cvclock);
		return (0);
	}

	/*
	 * Yes, we do, so send the data to either cvcredir or IOSRAM as
	 * appropriate.  In the latter case, we might not be able to transmit
	 * everything right now, so re-queue the remainder.
	 */
	if (cvcoutput_q != NULL && !via_iosram) {
		CVC_DBG0(CVC_DBG_NETWORK_WR, "Sending to cvcredir.");
		/*
		 * XXX - should canputnext be called here?  Starfire's cvc
		 * doesn't do that, and it appears to work anyway.
		 */
		(void) putnext(cvcoutput_q, total_mp);
	} else {
		CVC_DBG0(CVC_DBG_IOSRAM_WR, "Send to IOSRAM.");
		cvc_send_to_iosram(&total_mp);
		if (total_mp != NULL) {
			(void) putbq(q, total_mp);
		}
	}

	/*
	 * If there is still data queued at this point, make sure the queue
	 * gets scheduled again after an appropriate delay (which has been
	 * somewhat arbitrarily selected as half of the SC's input polling
	 * frequency).
	 */
	enableok(q);
	if (q->q_first != NULL) {
		if (cvc_timeout_id == (timeout_id_t)-1) {
			cvc_timeout_id = timeout(cvc_flush_queue,
			    NULL, drv_usectohz(CVC_IOSRAM_POLL_USECS / 2));
		}
	}
	rw_exit(&cvclock);
	return (0);
}


/*
 * cvc_ioctl()
 *	handle normal console ioctls.
 */
static void
cvc_ioctl(register queue_t *q, register mblk_t *mp)
{
	register cvc_t			*cp = q->q_ptr;
	int				datasize;
	int				error = 0;

	/*
	 * Let ttycommon_ioctl take the first shot at processing the ioctl.  If
	 * it fails because it can't allocate memory, schedule processing of the
	 * ioctl later when a proper buffer is available.  The mblk that
	 * couldn't be processed will have been stored in the tty structure by
	 * ttycommon_ioctl.
	 */
	datasize = ttycommon_ioctl(&cp->cvc_tty, q, mp, &error);
	if (datasize != 0) {
		if (cp->cvc_wbufcid) {
			unbufcall(cp->cvc_wbufcid);
		}
		cp->cvc_wbufcid = bufcall(datasize, BPRI_HI, cvc_reioctl, cp);
		return;
	}

	/*
	 * ttycommon_ioctl didn't do anything, but there's nothing we really
	 * support either with the exception of TCSBRK, which is supported
	 * only to appear a bit more like a serial device for software that
	 * expects TCSBRK to work.
	 */
	if (error != 0) {
		struct iocblk *iocp = (struct iocblk *)mp->b_rptr;

		if (iocp->ioc_cmd == TCSBRK) {
			miocack(q, mp, 0, 0);
		} else {
			miocnak(q, mp, 0, EINVAL);
		}
	} else {
		qreply(q, mp);
	}
}


/*
 * cvc_redir()
 *	called from cvcredir:cvcr_wput() to handle console input
 *	data. This routine puts the cvcredir write (downstream) data
 *	onto the cvc read (upstream) queues.
 */
int
cvc_redir(mblk_t *mp)
{
	register struct iocblk	*iocp;
	int			rv = 1;

	/*
	 * This function shouldn't be called if cvcredir hasn't registered yet.
	 */
	if (cvcinput_q == NULL) {
		/*
		 * Need to let caller know that it may be necessary for them to
		 * free the message buffer, so return 0.
		 */
		CVC_DBG0(CVC_DBG_REDIR, "redirection not enabled");
		cmn_err(CE_WARN, "cvc_redir: cvcinput_q NULL!");
		return (0);
	}

	CVC_DBG1(CVC_DBG_REDIR, "type 0x%x", mp->b_datap->db_type);
	if (mp->b_datap->db_type == M_DATA) {
		/*
		 * XXX - should canputnext be called here?  Starfire's cvc
		 * doesn't do that, and it appears to work anyway.
		 */
		CVC_DBG1(CVC_DBG_NETWORK_RD, "Sending mp 0x%x", mp);
		(void) putnext(cvcinput_q, mp);
	} else if (mp->b_datap->db_type == M_IOCTL) {
		/*
		 * The cvcredir driver filters out ioctl mblks we wouldn't
		 * understand, so we don't have to check for every conceivable
		 * ioc_cmd.  However, additional ioctls may be supported (again)
		 * some day, so the code is structured to check the value even
		 * though there's only one that is currently supported.
		 */
		iocp = (struct iocblk *)mp->b_rptr;
		if (iocp->ioc_cmd == CVC_DISCONNECT) {
			(void) putnextctl(cvcinput_q, M_HANGUP);
		}
	} else {
		/*
		 * Since we don't know what this mblk is, we're not going to
		 * process it.
		 */
		CVC_DBG1(CVC_DBG_REDIR, "unrecognized mblk type: %d",
		    mp->b_datap->db_type);
		rv = 0;
	}

	return (rv);
}


/*
 * cvc_register()
 *	called from cvcredir to register it's queues.  cvc
 *	receives data from cn via the streamhead and sends it to cvcredir
 *	via pointers to cvcredir's queues.
 */
int
cvc_register(queue_t *q)
{
	int error = -1;

	if (cvcinput_q == NULL)
		cmn_err(CE_WARN, "cvc_register: register w/ no console open!");
	rw_enter(&cvclock, RW_WRITER);
	if (cvcoutput_q == NULL) {
		cvcoutput_q = RD(q);  /* Make sure its the upstream q */
		qprocson(cvcoutput_q);	/* must be done within cvclock */
		error = 0;
	} else {
		/*
		 * cmn_err will call us, so release lock.
		 */
		rw_exit(&cvclock);
		if (cvcoutput_q == q)
			cmn_err(CE_WARN, "cvc_register: duplicate q!");
		else
			cmn_err(CE_WARN, "cvc_register: nondup q = 0x%p",
			    (void *)q);
		return (error);
	}
	rw_exit(&cvclock);
	return (error);
}


/*
 * cvc_unregister()
 *	called from cvcredir to clear pointers to its queues.
 *	cvcredir no longer wants to send or receive data.
 */
void
cvc_unregister(queue_t *q)
{
	rw_enter(&cvclock, RW_WRITER);
	if (q == cvcoutput_q) {
		qprocsoff(cvcoutput_q);	/* must be done within cvclock */
		cvcoutput_q = NULL;
	} else {
		rw_exit(&cvclock);
		cmn_err(CE_WARN, "cvc_unregister: q = 0x%p not registered",
		    (void *)q);
		return;
	}
	rw_exit(&cvclock);
}


/*
 * cvc_reioctl()
 *	Retry an "ioctl", now that "bufcall" claims we may be able
 *	to allocate the buffer we need.
 */
static void
cvc_reioctl(void *unit)
{
	register queue_t	*q;
	register mblk_t		*mp;
	register cvc_t		*cp = (cvc_t *)unit;

	/*
	 * The bufcall is no longer pending.
	 */
	if (!cp->cvc_wbufcid) {
		return;
	}
	cp->cvc_wbufcid = 0;
	if ((q = cp->cvc_tty.t_writeq) == NULL) {
		return;
	}
	if ((mp = cp->cvc_tty.t_iocpending) != NULL) {
		/* not pending any more */
		cp->cvc_tty.t_iocpending = NULL;
		cvc_ioctl(q, mp);
	}
}


/*
 * cvc_iosram_ops()
 *	Process commands sent to cvc from netcon_server via IOSRAM
 */
static void
cvc_iosram_ops(uint8_t op)
{
	int		rval = ESUCCESS;
	static uint8_t	stale_op = 0;

	ASSERT(MUTEX_HELD(&cvc_iosram_input_mutex));

	CVC_DBG1(CVC_DBG_IOSRAM_CNTL, "cntl msg 0x%x", op);

	/*
	 * If this is a repeated notice of a command that was previously
	 * processed but couldn't be cleared due to EAGAIN (tunnel switch in
	 * progress), just clear the data_valid flag and return.
	 */
	if (op == stale_op) {
		if (iosram_set_flag(IOSRAM_KEY_CONC, IOSRAM_DATA_INVALID,
		    IOSRAM_INT_NONE) == 0) {
			stale_op = 0;
		}
		return;
	}
	stale_op = 0;

	switch (op) {
		case CVC_IOSRAM_BREAK:		/* A console break (L1-A) */
			abort_sequence_enter((char *)NULL);
			break;

		case CVC_IOSRAM_DISCONNECT:	/* Break connection, hang up */
			if (cvcinput_q)
				(void) putnextctl(cvcinput_q, M_HANGUP);
			break;

		case CVC_IOSRAM_VIA_NET:	/* console via network */
			via_iosram = 0;
			break;

		case CVC_IOSRAM_VIA_IOSRAM:	/* console via iosram */
			via_iosram = 1;
			/*
			 * Tell cvcd to close any network connection it has.
			 */
			rw_enter(&cvclock, RW_READER);
			if (cvcoutput_q != NULL) {
				(void) putnextctl(cvcoutput_q, M_HANGUP);
			}
			rw_exit(&cvclock);
			break;

		case CVC_IOSRAM_WIN_RESIZE:	/* console window size data */
			/*
			 * In the case of window resizing, we don't want to
			 * record a stale_op value because we should always use
			 * the most recent winsize info, which could change
			 * between the time that we fail to clear the flag and
			 * the next time we try to process the command.  So,
			 * we'll just let cvc_win_resize clear the data_valid
			 * flag itself (hence the TRUE parameter) and not worry
			 * about whether or not it succeeds.
			 */
			cvc_win_resize(TRUE);
			return;
			/* NOTREACHED */

		default:
			cmn_err(CE_WARN, "cvc: unknown IOSRAM opcode %d", op);
			break;
	}

	/*
	 * Clear CONC's data_valid flag to indicate that the chunk is available
	 * for further communications.  If the flag can't be cleared due to an
	 * error, record the op value so we'll know to ignore it when we see it
	 * on the next poll.
	 */
	rval = iosram_set_flag(IOSRAM_KEY_CONC, IOSRAM_DATA_INVALID,
	    IOSRAM_INT_NONE);
	if (rval != 0) {
		stale_op = op;
		if (rval != EAGAIN) {
			cmn_err(CE_WARN,
			    "cvc_iosram_ops: set flag for cntlbuf ret %d",
			    rval);
		}
	}
}


/*
 * cvc_send_to_iosram()
 *	Flush as much data as possible to the CONO chunk.  If successful, free
 *	any mblks that were completely transmitted, update the b_rptr field in
 *	the first remaining mblk if it was partially transmitted, and update the
 *	caller's pointer to the new head of the mblk chain.  Since the software
 *	that will be pulling this data out of IOSRAM (dxs on the SC) is just
 *	polling at some frequency, we avoid attempts to flush data to IOSRAM any
 *	faster than a large divisor of that polling frequency.
 *
 *	Note that "cvc_buf_t out" is only declared "static" to keep it from
 *	being allocated on the stack.  Allocating 1K+ structures on the stack
 *	seems rather antisocial.
 */
static void
cvc_send_to_iosram(mblk_t **chainpp)
{
	int			rval;
	uint8_t			dvalid;
	uchar_t			*cp;
	mblk_t			*mp;
	mblk_t			*last_empty_mp;
	static clock_t		last_flush = (clock_t)-1;
	static cvc_buf_t	out;   /* see note above about static */

	ASSERT(chainpp != NULL);

	/*
	 * We _do_ have something to do, right?
	 */
	if (*chainpp == NULL) {
		return;
	}

	/*
	 * We can actually increase throughput by throttling back on attempts to
	 * flush data to IOSRAM, since trying to write every little bit of data
	 * as it shows up will actually generate more delays waiting for the SC
	 * to pick up each of those bits.  Instead, we'll avoid attempting to
	 * write data to IOSRAM any faster than half of the polling frequency we
	 * expect the SC to be using.
	 */
	if (ddi_get_lbolt() - last_flush <
	    drv_usectohz(CVC_IOSRAM_POLL_USECS / 2)) {
		return;
	}

	/*
	 * If IOSRAM is inaccessible or the CONO chunk still holds data that
	 * hasn't been picked up by the SC, there's nothing we can do right now.
	 */
	rval = iosram_get_flag(IOSRAM_KEY_CONO, &dvalid, NULL);
	if ((rval != 0) || (dvalid == IOSRAM_DATA_VALID)) {
		if ((rval != 0) && (rval != EAGAIN)) {
			cmn_err(CE_WARN, "cvc_send_to_iosram: get_flag ret %d",
			    rval);
		}
		return;
	}

	/*
	 * Copy up to MAX_XFER_COUTPUT chars from the mblk chain into a buffer.
	 * Don't change any of the mblks just yet, since we can't be certain
	 * that we'll be successful in writing data to the CONO chunk.
	 */
	out.count = 0;
	mp = *chainpp;
	cp = mp->b_rptr;
	last_empty_mp = NULL;
	while ((mp != NULL) && (out.count < MAX_XFER_COUTPUT)) {
		/*
		 * Process as many of the characters in the current mblk as
		 * possible.
		 */
		while ((cp != mp->b_wptr) && (out.count < MAX_XFER_COUTPUT)) {
			out.buffer[out.count++] = *cp++;
		}

		/*
		 * Did we process that entire mblk?  If so, move on to the next
		 * one.  If not, we're done filling the buffer even if there's
		 * space left, because apparently there wasn't room to process
		 * the next character.
		 */
		if (cp != mp->b_wptr) {
			break;
		}

		/*
		 * When this loop terminates, last_empty_mp will point to the
		 * last mblk that was completely processed, mp will point to the
		 * following mblk (or NULL if no more mblks exist), and cp will
		 * point to the first untransmitted character in the mblk
		 * pointed to by mp.  We'll need this data to update the mblk
		 * chain if all of the data is successfully transmitted.
		 */
		last_empty_mp = mp;
		mp = mp->b_cont;
		cp = (mp != NULL) ? mp->b_rptr : NULL;
	}

	/*
	 * If we succeeded in preparing some data, try to transmit it through
	 * IOSRAM.  First write the count and the data, which can be done in a
	 * single operation thanks to the buffer structure we use, then set the
	 * data_valid flag if the first step succeeded.
	 */
	if (out.count != 0) {
		rval = iosram_wr(IOSRAM_KEY_CONO, COUNT_OFFSET,
		    CONSBUF_COUNT_SIZE + out.count, (caddr_t)&out);
		if ((rval != 0) && (rval != EAGAIN)) {
			cmn_err(CE_WARN, "cvc_putc: write ret %d", rval);
		}

		/* if the data write succeeded, set the data_valid flag */
		if (rval == 0) {
			rval = iosram_set_flag(IOSRAM_KEY_CONO,
			    IOSRAM_DATA_VALID, IOSRAM_INT_NONE);
			if ((rval != 0) && (rval != EAGAIN)) {
				cmn_err(CE_WARN,
				    "cvc_putc: set flags for outbuf ret %d",
				    rval);
			}
		}

		/*
		 * If we successfully transmitted any data, modify the caller's
		 * mblk chain to remove the data that was transmitted, freeing
		 * all mblks that were completely processed.
		 */
		if (rval == 0) {
			last_flush = ddi_get_lbolt();

			/*
			 * If any data is left over, update the b_rptr field of
			 * the first remaining mblk in case some of its data was
			 * processed.
			 */
			if (mp != NULL) {
				mp->b_rptr = cp;
			}

			/*
			 * If any mblks have been emptied, unlink them from the
			 * residual chain, free them, and update the caller's
			 * mblk pointer.
			 */
			if (last_empty_mp != NULL) {
				last_empty_mp->b_cont = NULL;
				freemsg(*chainpp);
				*chainpp = mp;
			}
		}
	}
}


/*
 * cvc_flush_queue()
 *	Tell the STREAMS subsystem to schedule cvc_wsrv to process the queue we
 *	use to gather console output.
 */
/* ARGSUSED */
static void
cvc_flush_queue(void *notused)
{
	rw_enter(&cvclock, RW_WRITER);
	if (cvcinput_q != NULL) {
		qenable(WR(cvcinput_q));
	}

	cvc_timeout_id = (timeout_id_t)-1;
	rw_exit(&cvclock);
}


/*
 * cvc_getstr()
 *	Poll IOSRAM for console input while available.
 */
static void
cvc_getstr(char *cp)
{
	short		count;
	uint8_t		command = 0;
	int		rval = ESUCCESS;
	uint8_t		dvalid = IOSRAM_DATA_INVALID;
	uint8_t		intrpending = 0;

	mutex_enter(&cvc_iosram_input_mutex);
	while (dvalid == IOSRAM_DATA_INVALID) {
		/*
		 * Check the CONC data_valid flag to see if a control message is
		 * available.
		 */
		rval = iosram_get_flag(IOSRAM_KEY_CONC, &dvalid, &intrpending);
		if ((rval != 0) && (rval != EAGAIN)) {
			cmn_err(CE_WARN,
			    "cvc_getstr: get flag for cntl ret %d", rval);
		}

		/*
		 * If a control message is available, try to read and process
		 * it.
		 */
		if ((dvalid == IOSRAM_DATA_VALID) && (rval == 0)) {
			/* read the control reg offset */
			rval = iosram_rd(IOSRAM_KEY_CONC,
			    CVC_CTL_OFFSET(command), CVC_CTL_SIZE(command),
			    (caddr_t)&command);
			if ((rval != 0) && (rval != EAGAIN)) {
				cmn_err(CE_WARN,
				    "cvc_getstr: read for command ret %d",
				    rval);
			}

			/* process the cntl msg and clear the data_valid flag */
			if (rval == 0) {
				cvc_iosram_ops(command);
			}
		}

		/*
		 * Check the CONI data_valid flag to see if console input data
		 * is available.
		 */
		rval = iosram_get_flag(IOSRAM_KEY_CONI, &dvalid, &intrpending);
		if ((rval != 0) && (rval != EAGAIN)) {
			cmn_err(CE_WARN,
			    "cvc_getstr: get flag for inbuf ret %d",
			    rval);
		}
		if ((rval != 0) || (dvalid != IOSRAM_DATA_VALID)) {
			goto retry;
		}

		/*
		 * Try to read the count.
		 */
		rval = iosram_rd(IOSRAM_KEY_CONI, COUNT_OFFSET,
		    CONSBUF_COUNT_SIZE, (caddr_t)&count);
		if (rval != 0) {
			if (rval != EAGAIN) {
				cmn_err(CE_WARN,
				    "cvc_getstr: read for count ret %d", rval);
			}
			goto retry;
		}

		/*
		 * If there is data to be read, try to read it.
		 */
		if (count != 0) {
			rval = iosram_rd(IOSRAM_KEY_CONI, DATA_OFFSET, count,
			    (caddr_t)cp);
			if (rval != 0) {
				if (rval != EAGAIN) {
					cmn_err(CE_WARN,
					    "cvc_getstr: read for count ret %d",
					    rval);
				}
				goto retry;
			}
			cp[count] = '\0';
		}

		/*
		 * Try to clear the data_valid flag to indicate that whatever
		 * was in CONI was read successfully.  If successful, and some
		 * data was read, break out of the loop to return to the caller.
		 */
		rval = iosram_set_flag(IOSRAM_KEY_CONI, IOSRAM_DATA_INVALID,
		    IOSRAM_INT_NONE);
		if (rval != 0) {
			if (rval != EAGAIN) {
				cmn_err(CE_WARN,
				    "cvc_getstr: set flag for inbuf ret %d",
				    rval);
			}
		} else if (count != 0) {
			CVC_DBG1(CVC_DBG_IOSRAM_RD, "Read 0x%x", count);
			break;
		}

		/*
		 * Use a smaller delay between checks of IOSRAM for input
		 * when cvcd/cvcredir are not running or "via_iosram" has
		 * been set.
		 * We don't go away completely when i/o is going through the
		 * network via cvcd since a command may be sent via IOSRAM
		 * to switch if the network is down or hung.
		 */
retry:
		if ((cvcoutput_q == NULL) || (via_iosram))
			delay(drv_usectohz(CVC_IOSRAM_POLL_USECS));
		else
			delay(drv_usectohz(CVC_IOSRAM_POLL_USECS * 10));

	}

	mutex_exit(&cvc_iosram_input_mutex);
}


/*
 * cvc_input_daemon()
 *	this function runs as a separate kernel thread and polls IOSRAM for
 *	input, and possibly put it on read stream for the console.
 *	There are two poll rates (implemented in cvc_getstr):
 *		 100 000 uS (10 Hz) - no cvcd communications || via_iosram
 *		1000 000 uS ( 1 Hz) - cvcd communications
 * 	This continues to run even if there are network console communications
 *	in order to handle out-of-band signaling.
 */
/* ARGSUSED */
static void
cvc_input_daemon(void)
{
	char		linebuf[MAX_XFER_CINPUT + 1];
	char		*cp;
	mblk_t		*mbp;
	int		c;
	int		dropped_read = 0;

	for (;;) {
		cvc_getstr(linebuf);

		mbp = allocb(strlen(linebuf), BPRI_MED);
		if (mbp == NULL) {	/* drop it & go on if no buffer */
			if (!dropped_read) {
				cmn_err(CE_WARN, "cvc_input_daemon: "
				    "dropping IOSRAM reads");
			}
			dropped_read++;
			continue;
		}

		if (dropped_read) {
			cmn_err(CE_WARN,
			    "cvc_input_daemon: dropped %d IOSRAM reads",
			    dropped_read);
			dropped_read = 0;
		}

		for (cp = linebuf; *cp != '\0'; cp++) {
			c = (int)*cp;
			if (c == '\r')
				c = '\n';
			c &= 0177;
			*mbp->b_wptr = (char)c;
			mbp->b_wptr++;
		}
		mutex_enter(&cvcmutex);
		if (input_ok) {
			if (cvcinput_q == NULL) {
				cmn_err(CE_WARN,
				    "cvc_input_daemon: cvcinput_q is NULL!");
			} else {
				/*
				 * XXX - should canputnext be called here?
				 * Starfire's cvc doesn't do that, and it
				 * appears to work anyway.
				 */
				(void) putnext(cvcinput_q, mbp);
			}
		} else {
			freemsg(mbp);
		}
		mutex_exit(&cvcmutex);
	}

	/* NOTREACHED */
}

/*
 * cvc_win_resize()
 *	cvc_win_resize will read winsize data from the CONC IOSRAM chunk and set
 *	the console window size accordingly.  If indicated by the caller, CONC's
 *	data_valid flag will also be cleared.  The flag isn't cleared in all
 *	cases because we need to process winsize data at startup without waiting
 *	for a command.
 */
static void
cvc_win_resize(int clear_flag)
{
	int		rval;
	uint16_t	rows;
	uint16_t	cols;
	uint16_t	xpixels;
	uint16_t	ypixels;
	tty_common_t	*tty;
	cvc_t		*cp;
	struct winsize	ws;

	/*
	 * Start by reading the new window size out of the CONC chunk and, if
	 * requested, clearing CONC's data_valid flag.  If any of that fails,
	 * return immediately.  (Note that the rather bulky condition in the
	 * two "if" statements takes advantage of C's short-circuit logic
	 * evaluation)
	 */
	if (((rval = iosram_rd(IOSRAM_KEY_CONC, CVC_CTL_OFFSET(winsize_rows),
	    CVC_CTL_SIZE(winsize_rows), (caddr_t)&rows)) != 0) ||
	    ((rval = iosram_rd(IOSRAM_KEY_CONC, CVC_CTL_OFFSET(winsize_cols),
	    CVC_CTL_SIZE(winsize_cols), (caddr_t)&cols)) != 0) ||
	    ((rval = iosram_rd(IOSRAM_KEY_CONC,
	    CVC_CTL_OFFSET(winsize_xpixels), CVC_CTL_SIZE(winsize_xpixels),
	    (caddr_t)&xpixels)) != 0) || ((rval = iosram_rd(IOSRAM_KEY_CONC,
	    CVC_CTL_OFFSET(winsize_ypixels), CVC_CTL_SIZE(winsize_ypixels),
	    (caddr_t)&ypixels)) != 0)) {
		if (rval != EAGAIN) {
			cmn_err(CE_WARN,
			    "cvc_win_resize: read for ctlbuf ret %d", rval);
		}
		return;
	}

	if (clear_flag && ((rval = iosram_set_flag(IOSRAM_KEY_CONC,
	    IOSRAM_DATA_INVALID, IOSRAM_INT_NONE)) != 0)) {
		if (rval != EAGAIN) {
			cmn_err(CE_WARN,
			    "cvc_win_resize: set_flag for ctlbuf ret %d", rval);
		}
		return;
	}

	/*
	 * Copy the parameters from IOSRAM to a winsize struct.
	 */
	ws.ws_row = rows;
	ws.ws_col = cols;
	ws.ws_xpixel = xpixels;
	ws.ws_ypixel = ypixels;

	/*
	 * This code was taken from Starfire, and it appears to work correctly.
	 * However, since the original developer felt it necessary to add the
	 * following comment, it's probably worth preserving:
	 *
	 * XXX I hope this is safe...
	 */
	cp = cvcinput_q->q_ptr;
	tty = &cp->cvc_tty;
	mutex_enter(&tty->t_excl);
	if (bcmp((caddr_t)&tty->t_size, (caddr_t)&ws,
	    sizeof (struct winsize))) {
		tty->t_size = ws;
		mutex_exit(&tty->t_excl);
		(void) putnextctl1(cvcinput_q, M_PCSIG,
			SIGWINCH);
	} else {
		mutex_exit(&tty->t_excl);
	}
}

#ifdef DEBUG

void
cvc_dbg(uint32_t flag, char *fmt,
	uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
{
	char *s = NULL;
	char buf[256];

	if (cvc_dbg_flags && ((cvc_dbg_flags & flag) == flag)) {
		switch (flag) {
		case CVC_DBG_ATTACH:
			s = "attach";
			break;
		case CVC_DBG_DETACH:
			s = "detach";
			break;
		case CVC_DBG_OPEN:
			s = "open";
			break;
		case CVC_DBG_CLOSE:
			s = "close";
			break;
		case CVC_DBG_IOCTL:
			s = "ioctl";
			break;
		case CVC_DBG_REDIR:
			s = "redir";
			break;
		case CVC_DBG_WPUT:
			s = "wput";
			break;
		case CVC_DBG_WSRV:
			s = "wsrv";
			break;
		case CVC_DBG_IOSRAM_WR:
			s = "iosram_wr";
			break;
		case CVC_DBG_IOSRAM_RD:
			s = "iosram_rd";
			break;
		case CVC_DBG_NETWORK_WR:
			s = "network_wr";
			break;
		case CVC_DBG_NETWORK_RD:
			s = "network_rd";
			break;
		case CVC_DBG_IOSRAM_CNTL:
			s = "iosram_cntlmsg";
			break;
		default:
			s = "Unknown debug flag";
			break;
		}

		(void) sprintf(buf, "!%s_%s(%d): %s", ddi_driver_name(cvcdip),
		    s, cvc_instance, fmt);
		cmn_err(CE_NOTE, buf, a1, a2, a3, a4, a5);
	}
}

#endif /* DEBUG */