xref: /titanic_52/usr/src/uts/sun/io/ttymux/ttymux_ioctl.c (revision dd51520e127b452179a2ce4ea3bd8dee949f9afe)
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 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * DESCRIPTION
30  *
31  * ttymux_ioctl - Handler for ttymux specific ioctl calls.
32  *
33  */
34 
35 #include <sys/types.h>
36 #include <sys/strsubr.h>
37 #include <sys/strsun.h>
38 #include <sys/errno.h>
39 #include <sys/stat.h>
40 #include <sys/kmem.h>
41 #include <sys/ddi.h>
42 #include <sys/termio.h>
43 #include <sys/mkdev.h>
44 #include <sys/sunddi.h>
45 #include <sys/esunddi.h>
46 #include <sys/consdev.h>
47 #include <sys/promif.h>
48 
49 #include <sys/ttymux.h>
50 #include "ttymux_impl.h"
51 
52 /*
53  * Extern declarations
54  */
55 extern mblk_t *mkiocb(uint_t);
56 extern int nulldev();
57 extern uintptr_t space_fetch(char *key);
58 extern void prom_interpret(char *, uintptr_t, uintptr_t, uintptr_t,
59     uintptr_t, uintptr_t);
60 
61 /*
62  * Imported ttymux routines
63  */
64 extern void sm_debug(char *, ...);
65 extern void sm_log(char *, ...);
66 extern sm_lqi_t *get_lqi_byid(int);
67 extern sm_lqi_t *get_lqi_bydevt(dev_t);
68 extern int sm_associate(int, sm_lqi_t *, ulong_t, uint_t, char *);
69 extern int sm_disassociate(int, sm_lqi_t *, ulong_t);
70 
71 /*
72  * Exported ttymux routines
73  */
74 int ttymux_abort_ioctl(mblk_t *);
75 int ttymux_device_init(sm_lqi_t *);
76 int ttymux_device_fini(sm_lqi_t *);
77 int sm_ioctl_cmd(sm_uqi_t *, mblk_t *);
78 
79 /*
80  * Imported ttymux variables
81  */
82 extern sm_ss_t	*sm_ssp;
83 
84 static int
85 mblk2assoc(mblk_t *mp, ttymux_assoc_t *assoc)
86 {
87 	struct iocblk *iobp = (struct iocblk *)mp->b_rptr;
88 
89 	sm_dbg('M', ("mblk2assoc:\n"));
90 	if (mp->b_cont == NULL)
91 		return (EINVAL);
92 
93 #ifdef _SYSCALL32_IMPL
94 	if ((iobp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
95 		ttymux_assoc32_t *assoc32;
96 
97 		sm_dbg('I', ("mblk2assoc: b_cont 0x%p count %d (sz %d)\n",
98 		    mp->b_cont, iobp->ioc_count, sizeof (*assoc32)));
99 
100 		if (iobp->ioc_count < sizeof (ttymux_assoc32_t))
101 			return (EINVAL);
102 
103 		assoc32 = (ttymux_assoc32_t *)mp->b_cont->b_rptr;
104 		assoc->ttymux_udev = expldev(assoc32->ttymux32_udev);
105 		assoc->ttymux_ldev = expldev(assoc32->ttymux32_ldev);
106 		assoc->ttymux_linkid = assoc32->ttymux32_linkid;
107 		assoc->ttymux_tag = assoc32->ttymux32_tag;
108 		assoc->ttymux_ioflag = assoc32->ttymux32_ioflag;
109 		(void) strncpy(assoc->ttymux_path, assoc32->ttymux32_path,
110 				MAXPATHLEN);
111 
112 	} else
113 #endif
114 	if (iobp->ioc_count < sizeof (*assoc)) {
115 		return (EINVAL);
116 	} else {
117 		*assoc = *(ttymux_assoc_t *)mp->b_cont->b_rptr;
118 	}
119 	sm_dbg('M', ("mblk2assoc (%d): dev %d:%d not found\n",
120 	    assoc->ttymux_linkid, getmajor(assoc->ttymux_ldev),
121 			getminor(assoc->ttymux_ldev)));
122 	return (0);
123 }
124 
125 /*
126  * Given a device path return an OBP alias for it if it exists.
127  */
128 static char *
129 val2alias(pnode_t node, char *path)
130 {
131 	char *buf1;
132 	char *buf2;
133 	char *propname, *propval;
134 	int proplen;
135 
136 	if (node == OBP_BADNODE)
137 		return (NULL);
138 
139 	sm_dbg('A', ("Looking for an alias for: %s (len %d)\n",
140 	    path, strlen(path)));
141 
142 	/*
143 	 * Ask for first property by passing a NULL string
144 	 */
145 	buf1 = kmem_alloc(OBP_MAXPROPNAME, KM_SLEEP);
146 	buf2 = kmem_zalloc(OBP_MAXPROPNAME, KM_SLEEP);
147 	buf1[0] = '\0';
148 
149 	while (propname = (char *)prom_nextprop(node, buf1, buf2)) {
150 		if (strlen(propname) == 0)
151 			break;	  /* end of prop list */
152 
153 		(void) strcpy(buf1, propname);
154 
155 		proplen = prom_getproplen(node, propname);
156 		if (proplen == 0)
157 			continue;
158 		propval = kmem_zalloc(proplen + 1, KM_SLEEP);
159 		(void) prom_getprop(node, propname, propval);
160 
161 		if (strcmp(propval, path) == 0) {
162 			kmem_free(propval, proplen + 1);
163 			kmem_free(buf1, OBP_MAXPROPNAME);
164 			sm_dbg('A', ("Alias is : %s\n", buf2));
165 			return (buf2);
166 		}
167 
168 		kmem_free(propval, proplen + 1);
169 		bzero(buf2, OBP_MAXPROPNAME);
170 	}
171 
172 	kmem_free(buf1, OBP_MAXPROPNAME);
173 	kmem_free(buf2, OBP_MAXPROPNAME);
174 
175 	return (NULL);
176 }
177 
178 /*
179  * Tell OBP that this device is now usable
180  */
181 static void
182 enable_device(sm_mux_state_t *ms, sm_console_t *cn)
183 {
184 	char *enb_str = "\" enable-device\" rot $call-method";
185 
186 	if (!cn->sm_obp_con)
187 		return;
188 
189 	sm_dbg('A', ("ttymux: enabling %d:%d\n",
190 		getmajor(cn->sm_dev), getminor(cn->sm_dev)));
191 
192 	if (cn->sm_i_ihdl != 0)
193 		prom_interpret(enb_str, (caddr32_t)ms->sm_cons_stdin.sm_i_ihdl,
194 			(caddr32_t)cn->sm_i_ihdl, 0, 0, 0);
195 
196 	if (cn->sm_o_ihdl != 0 && cn->sm_o_ihdl != cn->sm_i_ihdl)
197 		prom_interpret(enb_str, (caddr32_t)ms->sm_cons_stdout.sm_o_ihdl,
198 			(caddr32_t)cn->sm_o_ihdl, 0, 0, 0);
199 }
200 
201 /*
202  * Tell OBP that this device is no longer usable
203  */
204 static void
205 disable_device(sm_mux_state_t *ms, sm_console_t *cn)
206 {
207 	char *dis_str = "\" disable-device\" rot $call-method";
208 
209 	if (!cn->sm_obp_con)
210 		return;
211 
212 	sm_dbg('A', ("ttymux: disabling %d:%d\n",
213 		getmajor(cn->sm_dev), getminor(cn->sm_dev)));
214 
215 	if (cn->sm_i_ihdl != 0)
216 		prom_interpret(dis_str, (caddr32_t)ms->sm_cons_stdin.sm_i_ihdl,
217 			(caddr32_t)cn->sm_i_ihdl, 0, 0, 0);
218 	if (cn->sm_o_ihdl != 0 && cn->sm_o_ihdl != cn->sm_i_ihdl)
219 		prom_interpret(dis_str, (caddr32_t)ms->sm_cons_stdout.sm_o_ihdl,
220 			(caddr32_t)cn->sm_o_ihdl, 0, 0, 0);
221 }
222 
223 static void
224 device_init_impl(sm_mux_state_t *ms, sm_console_t *cn, sm_lqi_t *plqi)
225 {
226 	uint_t		flags = 0;
227 	dev_info_t	*ldip;
228 
229 	sm_dbg('I', ("device_init_impl:\n"));
230 
231 	if (plqi == NULL || cn == NULL)
232 		return;
233 
234 	flags = (uint_t)cn->sm_mode;
235 	sm_dbg('I', ("device_init_impl: flgs %d con %d\n", flags,
236 			cn->sm_obp_con));
237 	if (ldip = e_ddi_hold_devi_by_dev(cn->sm_dev, 0)) {
238 
239 		/*
240 		 * Indicate to the linked device that it is
241 		 * providing a multiplexed console.
242 		 */
243 		if (flags & (uint_t)FORINPUT)
244 			(void) e_ddi_prop_create(cn->sm_dev, ldip,
245 			    DDI_PROP_CANSLEEP, "obp-input-console", 0, 0);
246 		if (flags & (uint_t)FOROUTPUT)
247 			(void) e_ddi_prop_create(cn->sm_dev, ldip,
248 			    DDI_PROP_CANSLEEP, "obp-output-console", 0, 0);
249 
250 		ddi_release_devi(ldip);
251 	}
252 
253 	if (flags) {
254 		plqi->sm_ioflag = flags;
255 		if (cn->sm_obp_con)
256 			plqi->sm_uqflags |= SM_OBPCNDEV;
257 		plqi->sm_ctrla_abort_on = sm_ssp->sm_ctrla_abort_on;
258 		plqi->sm_break_abort_on = sm_ssp->sm_break_abort_on;
259 	}
260 
261 	/*
262 	 * Tell OBP that its ok to use this console
263 	 */
264 	enable_device(ms, cn);
265 }
266 
267 static void
268 device_fini_impl(sm_mux_state_t *ms, sm_console_t *cn, sm_lqi_t *plqi)
269 {
270 	dev_info_t	*ldip;
271 
272 	if (plqi == NULL || cn == NULL)
273 		return;
274 	/*
275 	 * Indicate to the linked device that it is no longer
276 	 * providing a multiplexed console.
277 	 */
278 	if (ldip = e_ddi_hold_devi_by_dev(plqi->sm_dev, 0)) {
279 		if (plqi->sm_ioflag & (uint_t)FORINPUT)
280 			(void) e_ddi_prop_remove(plqi->sm_dev,
281 			    ldip, "obp-input-console");
282 		if (plqi->sm_ioflag & (uint_t)FOROUTPUT)
283 			(void) e_ddi_prop_remove(plqi->sm_dev,
284 			    ldip, "obp-output-console");
285 
286 		ddi_release_devi(ldip);
287 	}
288 	plqi->sm_ioflag = 0;
289 	plqi->sm_uqflags &= ~SM_OBPCNDEV;
290 	disable_device(ms, cn);
291 }
292 
293 static int
294 read_prop(pnode_t node, char *propname, char **propval)
295 {
296 	int	proplen = prom_getproplen(node, propname);
297 
298 	if (proplen < 0)
299 		return (proplen);
300 	*propval = kmem_zalloc(proplen + 1, KM_SLEEP);
301 	if (proplen > 0)
302 		(void) prom_getprop(node, propname, *propval);
303 	else
304 		*propval = 0;
305 
306 	return (proplen);
307 }
308 
309 /*
310  * Parse a list of tokens
311  */
312 static char *
313 sm_strtok_r(char *p, char *sep, char **lasts)
314 {
315 	char    *e, *tok = NULL;
316 
317 	if (p == 0 || *p == 0)
318 		return (NULL);
319 
320 	e = p + strlen(p);
321 
322 	do {
323 		if (strchr(sep, *p) != NULL) {
324 			if (tok != NULL) {
325 				*p = 0;
326 				*lasts = p + 1;
327 				return (tok);
328 			}
329 		} else if (tok == NULL) {
330 			tok = p;
331 		}
332 	} while (++p < e);
333 
334 	*lasts = NULL;
335 	if (tok != NULL)
336 		return (tok);
337 	return (NULL);
338 }
339 
340 /*
341  * Add or remove an alias from a property list of aliases:
342  * path:	an OBP device path
343  * pname:	property name containing a space separated list of aliases
344  * append:	if true include the alias for path in the property list
345  *		otherwise remove the alias from the list.
346  */
347 static int
348 upd_config(boolean_t append, char *pname, char *path)
349 {
350 	pnode_t		onode, anode;
351 	size_t		plen;		/* length of property name */
352 	char		*pval;		/* value of property */
353 	char		*tok, *lasts;
354 	char		*aliases[TTYMUX_MAX_LINKS];
355 	size_t		i, cnt, len;
356 	boolean_t	found;
357 	char		*nval, *alias = NULL;
358 
359 	if ((anode = prom_alias_node()) == OBP_BADNODE ||
360 	    (onode = prom_optionsnode()) == OBP_BADNODE) {
361 		sm_dbg('I', ("upd_config: no alias or options node.\n"));
362 		return (1);
363 	}
364 
365 	if ((plen = read_prop(onode, pname, &pval)) < 0)
366 		return (1);
367 
368 	sm_dbg('I', ("upd_config: %s=%s (%s)\n", pname, pval, path));
369 	found = B_FALSE;
370 	for (len = 0, cnt = 0, tok = sm_strtok_r(pval, " \t", &lasts);
371 	    tok != NULL && cnt < TTYMUX_MAX_LINKS;
372 	    tok = sm_strtok_r(lasts, " \t", &lasts)) {
373 		char	*aval;
374 		size_t	alen;
375 
376 		if ((alen = read_prop(anode, tok, &aval)) < 0)
377 			continue;
378 
379 		/* does this alias match the requested path */
380 		if (strcmp(aval, path) == 0 ||
381 		    (strstr(path, aval) != NULL &&
382 		    strchr(aval, ':') == NULL && strchr(path, ':') != NULL &&
383 		    strcmp(strchr(path, ':'), ":a") == 0)) {
384 			if (!found && append) {
385 				kmem_free(aval, alen + 1);
386 				goto out;
387 			}
388 			found = B_TRUE;
389 		} else {
390 			aliases[cnt++] = tok;
391 			len += strlen(tok) + 1;
392 		}
393 		kmem_free(aval, alen + 1);
394 	}
395 
396 	sm_dbg('I', ("%d aliases\n", cnt));
397 	if (append) {
398 		if (cnt + 1 == TTYMUX_MAX_LINKS)
399 			goto out;
400 
401 		if ((alias = val2alias(anode, path)) == NULL) {
402 			char *mnode = strstr(path, ":a");
403 
404 			if (mnode != 0) {
405 				*mnode = '\0';
406 				alias = val2alias(anode, path);
407 				*mnode = ':';
408 			}
409 		}
410 		if (alias == NULL) {
411 			sm_dbg('I', ("No alias for %s\n", path));
412 			goto out;
413 		}
414 		aliases[cnt++] = alias;
415 		len += strlen(alias) + 1;
416 	} else if (!found) {
417 		goto out;
418 	}
419 
420 	sm_dbg('I', ("%d aliases (len %d)\n", cnt, len));
421 	if (len == 0)
422 		goto out;
423 	ASSERT(len > 1 && cnt > 0);
424 
425 	nval = kmem_zalloc(len, KM_SLEEP);
426 	for (i = 0; ; ) {
427 		ASSERT(strlen(nval) + strlen(aliases[i]) + 1 <= len);
428 		sm_dbg('I', ("alias %s\n", aliases[i]));
429 		(void) strcat(nval, aliases[i]);
430 		if (++i == cnt)
431 			break;
432 		(void) strcat(nval, " ");
433 	}
434 
435 	sm_dbg('I', ("setprop: %s=%s (%d)\n", pname, nval, len));
436 
437 	(void) prom_setprop(onode, pname, nval, len);
438 
439 	kmem_free(nval, len);
440 
441 	if (alias != NULL)
442 		kmem_free(alias, OBP_MAXPROPNAME);
443 out:
444 	sm_dbg('I', ("upd_config: returning.\n"));
445 	kmem_free(pval, plen + 1);
446 	return (0);
447 }
448 
449 /*
450  *
451  */
452 static int
453 update_config(sm_mux_state_t *ms, char *path, io_mode_t mode, int cmd)
454 {
455 	sm_dbg('I', ("update_config: path %s io %d\n", path ? path : "", mode));
456 	if (path == 0 || *path == 0) {
457 		sm_dbg('I', ("update_config: EINVAL - no path\n"));
458 		return (1);
459 	}
460 	if (prom_is_openprom() == 0)
461 		return (0);
462 
463 	if ((mode & FORINPUT) && ms->sm_ialias != NULL)
464 		(void) upd_config((cmd == TTYMUX_ASSOC), ms->sm_ialias, path);
465 	if ((mode & FOROUTPUT) && ms->sm_oalias != NULL)
466 		(void) upd_config((cmd == TTYMUX_ASSOC), ms->sm_oalias, path);
467 	return (0);
468 }
469 
470 /*
471  * Convert a dev_t to a device path
472  */
473 static char *
474 sm_di_path(dev_t dev)
475 {
476 	char *p, *path;
477 
478 	if (dev == NODEV)
479 		return (NULL);
480 
481 	p = kmem_zalloc(MAXPATHLEN + 1, KM_SLEEP);
482 	if (ddi_dev_pathname(dev, S_IFCHR, p) == DDI_SUCCESS) {
483 		path = kmem_alloc(strlen(p) + 1, KM_SLEEP);
484 		(void) strcpy(path, p);
485 	}
486 	kmem_free(p, MAXPATHLEN + 1);
487 
488 	return (path);
489 }
490 
491 static int
492 console_cmd(int cmd, ttymux_assoc_t *assoc)
493 {
494 	sm_mux_state_t	*ms;
495 	sm_console_t	*cn;
496 	uint_t		j;
497 
498 	sm_dbg('I', ("console_cmd ENTER: %s\n", cmd == TTYMUX_DISASSOC ?
499 		"TTYMUX_DISASSOC" : "TTYMUX_ASSOC"));
500 
501 	if (assoc->ttymux_ldev == NODEV && *assoc->ttymux_path != '/') {
502 		sm_lqi_t *lqi = get_lqi_byid(assoc->ttymux_linkid);
503 		if (lqi == 0 || lqi->sm_dev == NODEV) {
504 			sm_dbg('I', ("console_cmd: no id link %d cmd %d\n",
505 			    assoc->ttymux_linkid, cmd));
506 			return (EINVAL);
507 		}
508 		assoc->ttymux_ldev = lqi->sm_dev;
509 	}
510 
511 	sm_dbg('I', ("console_cmd: path %s\n", assoc->ttymux_path));
512 
513 	if ((ms = (sm_mux_state_t *)space_fetch(TTYMUXPTR)) == 0) {
514 		sm_dbg('I', ("console_cmd: No muxstate\n"));
515 		return (0);
516 	}
517 
518 	mutex_enter(&ms->sm_cons_mutex);
519 
520 	for (cn = ms->sm_cons_links, j = 0;
521 	    j < ms->sm_cons_cnt; cn++, j++) {
522 		if (assoc->ttymux_ldev != NODEV && assoc->ttymux_ldev ==
523 				cn->sm_dev) {
524 			break;
525 		} else if (cn->sm_path != NULL &&
526 		    strncmp(cn->sm_path, assoc->ttymux_path, MAXPATHLEN) == 0) {
527 			break;
528 		}
529 	}
530 
531 	assoc->ttymux_path[MAXPATHLEN - 1] = 0;
532 	if (cmd == TTYMUX_DISASSOC) {
533 		if (j == ms->sm_cons_cnt) {
534 			mutex_exit(&ms->sm_cons_mutex);
535 			return (0);
536 		}
537 
538 		/*
539 		 * Disable the console in OBP and then delete this console
540 		 * this console - note that this also deletes OBP
541 		 * information - i.e. once it is disassociated it cannot
542 		 * be reused as an OBP console - roll on polled I/O!
543 		 */
544 		sm_dbg('I', ("console_cmd: cleaning up\n"));
545 		device_fini_impl(ms, cn, get_lqi_bydevt(assoc->ttymux_ldev));
546 
547 		if (cn->sm_path == NULL) {
548 			if (assoc->ttymux_ldev != NODEV)
549 				cn->sm_path = sm_di_path(assoc->ttymux_ldev);
550 			else
551 				(void) update_config(ms, assoc->ttymux_path,
552 				    assoc->ttymux_ioflag, cmd);
553 		}
554 		if (cn->sm_path) {
555 			(void) update_config(ms, cn->sm_path, cn->sm_mode, cmd);
556 			kmem_free(cn->sm_path, strlen(cn->sm_path) + 1);
557 			cn->sm_path = NULL;
558 		}
559 		ms->sm_cons_cnt -= 1;
560 		if (ms->sm_cons_cnt > 0)
561 			*cn = ms->sm_cons_links[ms->sm_cons_cnt];
562 
563 		sm_dbg('I', ("console_cmd: console %d removed (cnt %d)\n",
564 		    j, ms->sm_cons_cnt));
565 
566 	} else if (cmd == TTYMUX_ASSOC) {
567 
568 		if (j == ms->sm_cons_cnt) {
569 
570 			if (j == TTYMUX_MAX_LINKS) {
571 				mutex_exit(&ms->sm_cons_mutex);
572 				return (ENOMEM);
573 			}
574 
575 			ms->sm_cons_cnt += 1;
576 
577 			bzero((caddr_t)cn, sizeof (*cn));
578 			cn->sm_dev = assoc->ttymux_ldev;
579 			cn->sm_muxid = assoc->ttymux_linkid;
580 			cn->sm_mode = assoc->ttymux_ioflag;
581 			device_init_impl(ms, cn,
582 				get_lqi_bydevt(assoc->ttymux_ldev));
583 		} else {
584 			cn->sm_dev = assoc->ttymux_ldev;
585 			cn->sm_muxid = assoc->ttymux_linkid;
586 			cn->sm_mode = assoc->ttymux_ioflag;
587 		}
588 
589 		if (assoc->ttymux_ldev != NODEV) {
590 			cn->sm_path = sm_di_path(assoc->ttymux_ldev);
591 		} else {
592 			cn->sm_path = kmem_alloc(strlen(assoc->ttymux_path) + 1,
593 			    KM_SLEEP);
594 			(void) strcpy(cn->sm_path, assoc->ttymux_path);
595 		}
596 		if (cn->sm_path != NULL)
597 			(void) update_config(ms, cn->sm_path, cn->sm_mode, cmd);
598 		else
599 			sm_dbg('I', ("console_cmd: ASSOC No path info"));
600 	}
601 	mutex_exit(&ms->sm_cons_mutex);
602 	sm_dbg('I', ("console_cmd EXIT: %s\n", cmd == TTYMUX_DISASSOC ?
603 		"TTYMUX_DISASSOC" : "TTYMUX_ASSOC"));
604 	return (0);
605 }
606 
607 static int
608 get_unconfigured_consoles(sm_mux_state_t *ms, ttymux_assoc_t *a)
609 {
610 	sm_console_t	*cn;
611 	int		j, cnt;
612 
613 	if (ms == 0)
614 		return (0);
615 
616 	mutex_enter(&ms->sm_cons_mutex);
617 	for (cn = ms->sm_cons_links, cnt = j = 0; j < ms->sm_cons_cnt;
618 							cn++, j++) {
619 		if (cn->sm_path && get_lqi_bydevt(cn->sm_dev) == NULL) {
620 			a->ttymux_linkid = cn->sm_muxid;
621 			a->ttymux_tag = (uint_t)0;
622 			a->ttymux_ioflag = cn->sm_mode;
623 			a->ttymux_udev = cn->sm_mode & FORINPUT ?
624 						ms->sm_cons_stdin.sm_dev :
625 						ms->sm_cons_stdout.sm_dev;
626 			a->ttymux_ldev = NODEV;
627 			(void) strncpy(a->ttymux_path, cn->sm_path, MAXPATHLEN);
628 			cnt++;
629 			a++;
630 		}
631 	}
632 	mutex_exit(&ms->sm_cons_mutex);
633 	return (cnt);
634 }
635 
636 #ifdef _SYSCALL32_IMPL
637 /*
638  * Look for any consoles that are not currently plumbed under the multiplexer.
639  */
640 static int
641 get_unconfigured_consoles32(sm_mux_state_t *ms, ttymux_assoc32_t *a)
642 {
643 	sm_console_t	*cn;
644 	int		j, cnt;
645 
646 	if (ms == 0)
647 		return (0);
648 
649 	mutex_enter(&ms->sm_cons_mutex);
650 	for (cn = ms->sm_cons_links, cnt = j = 0; j < ms->sm_cons_cnt;
651 							cn++, j++) {
652 		sm_dbg('I', ("get_unconfigured_consoles: check %s (%d:%d)",
653 		    cn->sm_path ? cn->sm_path : "NULL",
654 		    getmajor(cn->sm_dev), getminor(cn->sm_dev)));
655 		if (cn->sm_path && get_lqi_bydevt(cn->sm_dev) == NULL) {
656 			a->ttymux32_linkid = 0;
657 			a->ttymux32_tag = (uint32_t)0;
658 			a->ttymux32_ioflag = (uint32_t)cn->sm_mode;
659 			a->ttymux32_ldev = NODEV32;
660 			(void) cmpldev(&a->ttymux32_udev, cn->sm_mode &
661 					FORINPUT ? ms->sm_cons_stdin.sm_dev :
662 					ms->sm_cons_stdout.sm_dev);
663 
664 			(void) strncpy(a->ttymux32_path, cn->sm_path,
665 					MAXPATHLEN);
666 			cnt++;
667 			a++;
668 		}
669 	}
670 	mutex_exit(&ms->sm_cons_mutex);
671 	return (cnt);
672 }
673 #endif
674 
675 static int
676 count_unconfigured_consoles(sm_mux_state_t *ms)
677 {
678 	sm_console_t	*cn;
679 	int		j, cnt;
680 
681 	if (ms == 0)
682 		return (0);
683 
684 	mutex_enter(&ms->sm_cons_mutex);
685 	for (cn = ms->sm_cons_links, cnt = j = 0; j < ms->sm_cons_cnt;
686 							cn++, j++) {
687 		sm_dbg('I', ("cnt_unconfigured_consoles: check %s (%d:%d)",
688 		    cn->sm_path ? cn->sm_path : "NULL",
689 		    getmajor(cn->sm_dev), getminor(cn->sm_dev)));
690 		if (cn->sm_path && get_lqi_bydevt(cn->sm_dev) == NULL)
691 			cnt++;
692 	}
693 	mutex_exit(&ms->sm_cons_mutex);
694 	return (cnt);
695 }
696 
697 /*
698  * Exported interfaces
699  */
700 
701 /*
702  * A console device is no longer associated.
703  */
704 int
705 ttymux_device_fini(sm_lqi_t *plqi)
706 {
707 	int		j;
708 	sm_mux_state_t	*ms;
709 
710 	ms = (sm_mux_state_t *)space_fetch(TTYMUXPTR);
711 
712 	if (plqi == NULL || ms == NULL)
713 		return (0);
714 
715 	mutex_enter(&ms->sm_cons_mutex);
716 
717 	for (j = 0; j < ms->sm_cons_cnt; j++) {
718 
719 		if (ms->sm_cons_links[j].sm_dev == plqi->sm_dev) {
720 
721 			device_fini_impl(ms, &ms->sm_cons_links[j], plqi);
722 
723 			mutex_exit(&ms->sm_cons_mutex);
724 			return (0);
725 		}
726 	}
727 	mutex_exit(&ms->sm_cons_mutex);
728 
729 	return (1);
730 }
731 
732 /*
733  * A console device is being introduced.
734  */
735 int
736 ttymux_device_init(sm_lqi_t *plqi)
737 {
738 	int j;
739 	sm_mux_state_t *ms;
740 
741 	ms = (sm_mux_state_t *)space_fetch(TTYMUXPTR);
742 
743 	if (ms == NULL)
744 		return (0);
745 
746 	mutex_enter(&ms->sm_cons_mutex);
747 
748 	for (j = 0; j < ms->sm_cons_cnt; j++) {
749 
750 		if (ms->sm_cons_links[j].sm_dev == plqi->sm_dev) {
751 
752 			device_init_impl(ms, &ms->sm_cons_links[j], plqi);
753 
754 			mutex_exit(&ms->sm_cons_mutex);
755 			return (0);
756 		}
757 	}
758 	mutex_exit(&ms->sm_cons_mutex);
759 	return (1);
760 }
761 
762 /*
763  * Process a TTYMUX_ASSOCIATE or TTYMUX_DISASSOCIATE ioctl.
764  */
765 static int
766 ttymux_link_ioctl(mblk_t *mp)
767 {
768 	ttymux_assoc_t	assoc;
769 	int		err;
770 	sm_lqi_t		*lqi;
771 	struct iocblk	*iobp = (struct iocblk *)mp->b_rptr;
772 	dev_t		cidev, codev;
773 
774 	sm_dbg('I', ("ttymux_link_ioctl:\n"));
775 	if ((err = mblk2assoc(mp, &assoc)) != 0)
776 		return (err);
777 
778 	sm_dbg('I', ("uminor is %d\n", getminor(assoc.ttymux_udev)));
779 
780 	if (assoc.ttymux_udev == NODEV)
781 		return (EINVAL);
782 
783 	err = 0;
784 
785 	if ((lqi = get_lqi_bydevt(assoc.ttymux_ldev)) == NULL) {
786 		if (assoc.ttymux_linkid < 0)
787 			err = EINVAL;
788 		else if ((lqi = get_lqi_byid(assoc.ttymux_linkid)) == 0)
789 			err = ENOLINK;
790 	}
791 
792 	if (sm_ssp->sm_ms) {
793 		mutex_enter(&sm_ssp->sm_ms->sm_cons_mutex);
794 		cidev = sm_ssp->sm_ms->sm_cons_stdin.sm_dev;
795 		codev = sm_ssp->sm_ms->sm_cons_stdout.sm_dev;
796 		mutex_exit(&sm_ssp->sm_ms->sm_cons_mutex);
797 	} else {
798 		cidev = codev = NODEV;
799 	}
800 
801 	if (err != 0) {
802 		if (assoc.ttymux_udev != cidev && assoc.ttymux_udev != codev)
803 			return (err);
804 		(void) console_cmd(iobp->ioc_cmd, &assoc);
805 		return (0);
806 	} else if (assoc.ttymux_udev == cidev || assoc.ttymux_udev == codev) {
807 		(void) console_cmd(iobp->ioc_cmd, &assoc);
808 	}
809 
810 	if (iobp->ioc_cmd == TTYMUX_ASSOC)
811 		return (sm_associate(sm_dev2unit(assoc.ttymux_udev),
812 		    lqi, assoc.ttymux_tag, assoc.ttymux_ioflag,
813 			assoc.ttymux_path));
814 	else if (iobp->ioc_cmd == TTYMUX_DISASSOC)
815 		return (sm_disassociate(sm_dev2unit(assoc.ttymux_udev),
816 		    lqi, assoc.ttymux_tag));
817 
818 	return (0);
819 }
820 
821 /*
822  * Process a TTYMUX_GETLINK ioctl.
823  */
824 int
825 ttymux_query_link_ioctl(mblk_t *mp)
826 {
827 	sm_lqi_t		*lqi;
828 
829 	struct iocblk *iobp = (struct iocblk *)mp->b_rptr;
830 
831 	sm_dbg('I', ("ttymux_query_link_ioctl:\n"));
832 
833 	if (mp->b_cont == NULL)
834 		return (EINVAL);
835 
836 #ifdef _SYSCALL32_IMPL
837 	if ((iobp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
838 		ttymux_assoc32_t	*assoc32;
839 		ttymux_assoc_t	assoc;
840 
841 		if (mblk2assoc(mp, &assoc) != 0)
842 			return (EINVAL);
843 
844 		if ((lqi = get_lqi_bydevt(assoc.ttymux_ldev)) == NULL &&
845 		    (lqi = get_lqi_byid(assoc.ttymux_linkid)) == NULL) {
846 			sm_dbg('M', ("Query Link (%d): dev %d:%d not found\n",
847 			    assoc.ttymux_linkid,
848 			    getmajor(assoc.ttymux_ldev),
849 				getminor(assoc.ttymux_ldev)));
850 			return (ENOLINK);
851 		}
852 		assoc32 = (ttymux_assoc32_t *)mp->b_cont->b_rptr;
853 		LQI2ASSOC32(assoc32, lqi);
854 	} else
855 #endif
856 	{
857 		ttymux_assoc_t	*assoc;
858 
859 		if (iobp->ioc_count < sizeof (ttymux_assoc_t))
860 			return (EINVAL);
861 
862 		assoc = (ttymux_assoc_t *)mp->b_cont->b_rptr;
863 		if ((lqi = get_lqi_bydevt(assoc->ttymux_ldev)) == NULL &&
864 		    (lqi = get_lqi_byid(assoc->ttymux_linkid)) == NULL) {
865 			return (ENOLINK);
866 		}
867 		LQI2ASSOC(assoc, lqi);
868 	}
869 	return (0);
870 }
871 
872 /*
873  * Response to receiving an M_IOCDATA message for the TTYMUX_LIST ioctl.
874  */
875 static int
876 sm_iocresp(mblk_t *mp)
877 {
878 	struct copyresp *csp = (struct copyresp *)mp->b_rptr;
879 	struct iocblk	*iobp = (struct iocblk *)mp->b_rptr;
880 	mblk_t		*pmp;
881 
882 	sm_dbg('M', ("(M_IOCDATA: cmd %d)\n", csp->cp_cmd));
883 
884 	if (csp->cp_cmd != TTYMUX_LIST) {
885 		sm_dbg('M', ("(M_IOCDATA: unknown cmd)\n"));
886 		DB_TYPE(mp) = M_IOCNAK;
887 		return (EINVAL);
888 	}
889 	if (csp->cp_rval) {
890 		if (csp->cp_private)
891 			freemsg((mblk_t *)csp->cp_private);
892 
893 		sm_dbg('M', ("M_IOCDATA: result is %d\n", csp->cp_rval));
894 		DB_TYPE(mp) = M_IOCNAK;
895 		iobp->ioc_error = (int)(uintptr_t)csp->cp_rval;
896 		iobp->ioc_rval = 0;
897 		return (iobp->ioc_error);
898 	}
899 
900 	pmp = (mblk_t *)csp->cp_private;
901 
902 #ifdef _SYSCALL32_IMPL
903 	if ((csp->cp_flag & IOC_MODELS) != IOC_NATIVE) {
904 		iobp->ioc_count = sizeof (ttymux_assocs32_t);
905 		iobp->ioc_rval = pmp == NULL ? 0 :
906 		    ((ttymux_assocs32_t *)pmp->b_rptr)->ttymux32_nlinks;
907 	} else
908 #endif
909 	{
910 		iobp->ioc_count = sizeof (ttymux_assocs_t);
911 		iobp->ioc_rval = pmp == NULL ? 0 :
912 		    ((ttymux_assocs_t *)pmp->b_rptr)->ttymux_nlinks;
913 
914 	}
915 
916 	DB_TYPE(mp) = (pmp) ? M_IOCACK : M_IOCNAK;
917 	mp->b_wptr = mp->b_rptr + sizeof (struct iocblk);
918 
919 	if (mp->b_cont)
920 		freemsg(unlinkb(mp));
921 	if (pmp)
922 		linkb(mp, pmp);
923 	else
924 		iobp->ioc_count = 0;
925 
926 	iobp->ioc_error = 0;
927 
928 	sm_dbg('M', ("(M_IOCDATA: rval %d cnt %d private 0x%p)\n",
929 	    iobp->ioc_rval, iobp->ioc_count, pmp));
930 	return (0);
931 }
932 
933 /*
934  * Process a TTYMUX_LIST ioctl.
935  */
936 int
937 ttymux_query_links_ioctl(mblk_t *mp)
938 {
939 	struct iocblk	*iobp = (struct iocblk *)mp->b_rptr;
940 	struct copyreq	*cqp;
941 	int		unit;
942 	sm_lqi_t		*lqi;
943 	mblk_t		*nmp;
944 	int		cnt;
945 	void		*asl;
946 	void		*uaddr;
947 	size_t		sz;
948 
949 	if (DB_TYPE(mp) == M_IOCDATA) {
950 		return (sm_iocresp(mp));
951 	}
952 	/*
953 	 * Is this a query for the number of linked devices?
954 	 */
955 	if (iobp->ioc_count == 0) {
956 
957 		for (unit = 0, iobp->ioc_rval = 0;
958 		    unit < MAX_LQS && (lqi = get_lqi(sm_ssp, unit));
959 		    unit++)
960 			if (lqi->sm_linkid != 0)
961 				iobp->ioc_rval += 1;
962 
963 		iobp->ioc_rval += count_unconfigured_consoles(sm_ssp->sm_ms);
964 		DB_TYPE(mp) = M_IOCACK;
965 		iobp->ioc_error = 0;
966 
967 		return (0);
968 	}
969 
970 	if (mp->b_cont == NULL) {
971 		sm_dbg('Y', ("TTYMUX_LIST: b_cont is NULL\n"));
972 		DB_TYPE(mp) = M_IOCNAK;
973 		iobp->ioc_error = EINVAL;
974 		return (EINVAL);
975 	}
976 
977 	asl = mp->b_cont->b_rptr;
978 
979 #ifdef _SYSCALL32_IMPL
980 	if ((iobp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
981 		cnt = ((ttymux_assocs32_t *)asl)->ttymux32_nlinks;
982 		sz = cnt * sizeof (ttymux_assoc32_t);
983 		uaddr = (void *)(uintptr_t)
984 		    ((ttymux_assocs32_t *)asl)->ttymux32_assocs;
985 	} else
986 #endif
987 	{
988 		cnt = ((ttymux_assocs_t *)asl)->ttymux_nlinks;
989 		sz = cnt * sizeof (ttymux_assoc_t);
990 		uaddr = (void *)((ttymux_assocs_t *)asl)->ttymux_assocs;
991 	}
992 	if ((nmp = sm_allocb(sz, BPRI_MED)) == NULL) {
993 		DB_TYPE(mp) = M_IOCNAK;
994 		iobp->ioc_error = EINVAL;
995 		return (EAGAIN);
996 	}
997 
998 	sm_dbg('Y', ("TTYMUX_LIST: cnt %d sz %d uaddr 0x%p\n", cnt, sz, uaddr));
999 
1000 	iobp->ioc_rval = 0;
1001 
1002 #ifdef _SYSCALL32_IMPL
1003 	if ((iobp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
1004 		ttymux_assoc32_t	*assoc;
1005 
1006 		sm_dbg('Y', ("!Native: %d structures\n", cnt));
1007 		assoc = (ttymux_assoc32_t *)nmp->b_rptr;
1008 
1009 		for (unit = 0;
1010 		    unit < MAX_LQS && (lqi = get_lqi(sm_ssp, unit));
1011 		    unit++) {
1012 			if (lqi->sm_linkid != 0) {
1013 				if (cnt-- == 0)
1014 					break;
1015 				LQI2ASSOC32(assoc, lqi);
1016 				assoc++;
1017 				iobp->ioc_rval += 1;
1018 			}
1019 		}
1020 		if (cnt > 0) {
1021 			/* see if there are unconfigured consoles */
1022 			iobp->ioc_rval +=
1023 			    get_unconfigured_consoles32(sm_ssp->sm_ms, assoc);
1024 			sm_dbg('I', ("%d unconfigured consoles\n",
1025 			    iobp->ioc_rval));
1026 		} else {
1027 			sm_dbg('I', ("no more space in user addr\n"));
1028 		}
1029 		((ttymux_assocs32_t *)asl)->ttymux32_nlinks = iobp->ioc_rval;
1030 	} else
1031 #endif
1032 	{
1033 		ttymux_assoc_t	*assoc;
1034 
1035 		sm_dbg('Y', ("!Native: %d structures\n", cnt));
1036 		assoc = (ttymux_assoc_t *)nmp->b_wptr;
1037 
1038 		for (unit = 0;
1039 		    unit < MAX_LQS && (lqi = get_lqi(sm_ssp, unit));
1040 		    unit++) {
1041 			if (lqi->sm_linkid != 0) {
1042 				if (cnt-- == 0)
1043 					break;
1044 				LQI2ASSOC(assoc, lqi);
1045 				assoc++;
1046 				iobp->ioc_rval += 1;
1047 			}
1048 		}
1049 		if (cnt > 0) {
1050 			/* see if there are unconfigured consoles */
1051 			iobp->ioc_rval +=
1052 			    get_unconfigured_consoles(sm_ssp->sm_ms, assoc);
1053 			sm_dbg('I', ("%d unconfigured consoles\n",
1054 			    iobp->ioc_rval));
1055 		} else {
1056 			sm_dbg('I', ("no more space in user addr\n"));
1057 		}
1058 		((ttymux_assocs_t *)asl)->ttymux_nlinks = iobp->ioc_rval;
1059 	}
1060 
1061 	cqp = (struct copyreq *)mp->b_rptr;
1062 	cqp->cq_addr = uaddr;
1063 	cqp->cq_size = sz;
1064 	cqp->cq_flag = 0;
1065 	cqp->cq_private = mp->b_cont;
1066 	mp->b_cont = nmp;
1067 	nmp->b_wptr = nmp->b_rptr + sz;
1068 
1069 	DB_TYPE(mp) = M_COPYOUT;
1070 	mp->b_wptr = mp->b_rptr + sizeof (struct copyreq);
1071 
1072 	return (0);
1073 }
1074 
1075 /*
1076  * Process a TTYMUX_CONSDEV ioctl.
1077  */
1078 static int
1079 ttymux_console_ioctl(mblk_t *mp)
1080 {
1081 	struct iocblk *iobp = (struct iocblk *)mp->b_rptr;
1082 	int	err = EINVAL;
1083 
1084 	sm_dbg('I', ("ttymux_console_ioctl:\n"));
1085 #ifdef _SYSCALL32_IMPL
1086 	if ((iobp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
1087 		if (mp->b_cont && iobp->ioc_count >= sizeof (dev32_t)) {
1088 			dev32_t dev;
1089 
1090 			(void) cmpldev(&dev, rconsdev);
1091 
1092 			*(dev32_t *)mp->b_cont->b_rptr = dev;
1093 			mp->b_cont->b_wptr = mp->b_cont->b_rptr + sizeof (dev);
1094 			iobp->ioc_count = sizeof (dev);
1095 			err = 0;
1096 		} else {
1097 			sm_dbg('I', ("TTYMUX_CONSDEV: b_cont 0x%p count %d\n",
1098 			    mp->b_cont, iobp->ioc_count));
1099 		}
1100 	} else
1101 #endif
1102 	if (mp->b_cont && iobp->ioc_count >= sizeof (dev_t)) {
1103 		*(dev_t *)mp->b_cont->b_rptr = rconsdev;
1104 		mp->b_cont->b_wptr = mp->b_cont->b_rptr + sizeof (rconsdev);
1105 		iobp->ioc_count = sizeof (rconsdev);
1106 		err = 0;
1107 	}
1108 	return (err);
1109 }
1110 
1111 /*
1112  * Process a ioctl relating to aborting on the console.
1113  */
1114 int
1115 ttymux_abort_ioctl(mblk_t *mp)
1116 {
1117 	struct iocblk	*iobp;
1118 	int		cmd, err = 0;
1119 	sm_lqi_t		*lqi;
1120 	ttymux_abort_t	*abreq;
1121 #ifdef _SYSCALL32_IMPL
1122 	struct ttymux_abort32 {
1123 		dev32_t			ldev;
1124 		enum ttymux_break_type  method;
1125 		uint32_t		enable;
1126 	} *abreq32;
1127 #endif
1128 	dev_t			ldev;
1129 	enum ttymux_break_type  method;
1130 	uint_t			enable;
1131 
1132 	iobp = (struct iocblk *)mp->b_rptr;
1133 	cmd = iobp->ioc_cmd;
1134 
1135 	iobp->ioc_error = 0;
1136 	iobp->ioc_rval = 0;
1137 	sm_dbg('I', ("ttymux_abort_ioctl:\n"));
1138 	switch (cmd) {
1139 	case CONSSETABORTENABLE:
1140 		lqi = (sm_ssp->sm_lconsole) ? sm_ssp->sm_lconsole->sm_lqs : 0;
1141 		enable = (*(intptr_t *)mp->b_cont->b_rptr) ? 1 : 0;
1142 		sm_ssp->sm_ctrla_abort_on = sm_ssp->sm_break_abort_on = enable;
1143 		for (; lqi != 0; lqi = lqi->sm_nlqi) {
1144 			lqi->sm_ctrla_abort_on = enable;
1145 			lqi->sm_break_abort_on = enable;
1146 		}
1147 		break;
1148 	case CONSGETABORTENABLE:
1149 		if (mp->b_cont == 0 || iobp->ioc_count < sizeof (intptr_t)) {
1150 			iobp->ioc_error = EINVAL;
1151 			iobp->ioc_rval = -1;
1152 		} else {
1153 			*(intptr_t *)mp->b_cont->b_rptr =
1154 			    (sm_ssp->sm_ctrla_abort_on ||
1155 			    sm_ssp->sm_break_abort_on);
1156 			mp->b_cont->b_wptr =
1157 				mp->b_cont->b_rptr + sizeof (intptr_t);
1158 			iobp->ioc_count = sizeof (intptr_t);
1159 		}
1160 		break;
1161 	case TTYMUX_GETABORTSTR:
1162 
1163 		if (iobp->ioc_count < strlen(sm_ssp->sm_abs) + 1 ||
1164 		    mp->b_cont == 0 ||
1165 		    mp->b_cont->b_cont) {
1166 			iobp->ioc_error = EINVAL;
1167 			iobp->ioc_rval = -1;
1168 		} else {
1169 			(void) strcpy((char *)mp->b_cont->b_rptr,
1170 					sm_ssp->sm_abs);
1171 			iobp->ioc_count = strlen(sm_ssp->sm_abs) + 1;
1172 			mp->b_cont->b_wptr =
1173 				mp->b_cont->b_rptr + iobp->ioc_count;
1174 		}
1175 		break;
1176 	case TTYMUX_GETABORT:
1177 	case TTYMUX_SETABORT:
1178 
1179 		lqi = 0;
1180 #ifdef _SYSCALL32_IMPL
1181 		if ((iobp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
1182 			if (iobp->ioc_count < sizeof (*abreq32) ||
1183 			    mp->b_cont == 0) {
1184 				err = EINVAL;
1185 			} else {
1186 				abreq32 = (struct ttymux_abort32 *)
1187 				    mp->b_cont->b_rptr;
1188 				ldev = expldev(abreq32->ldev);
1189 				method = abreq32->method;
1190 				enable = (uint_t)abreq32->enable;
1191 				iobp->ioc_count = sizeof (*abreq32);
1192 			}
1193 		} else
1194 #endif
1195 		if (iobp->ioc_count < sizeof (*abreq) ||
1196 		    mp->b_cont == 0) {
1197 			err = EINVAL;
1198 		} else {
1199 			abreq = (ttymux_abort_t *)mp->b_cont->b_rptr;
1200 			ldev = abreq->ttymux_ldev;
1201 			method = abreq->ttymux_method;
1202 			enable = abreq->ttymux_enable;
1203 			iobp->ioc_count = sizeof (*abreq);
1204 		}
1205 
1206 		if (err != 0) {
1207 			iobp->ioc_rval = -1;
1208 			return ((iobp->ioc_error = err));
1209 		}
1210 
1211 		sm_dbg('Y', ("ttymux_abort_ioctl: type %d how %d ldev %d:%d\n",
1212 		    method, enable, getmajor(ldev), getminor(ldev)));
1213 
1214 		lqi = get_lqi_bydevt(ldev);
1215 		if (ldev != NODEV && lqi == 0) {
1216 			err = ENOLINK;
1217 		} else if (cmd == TTYMUX_GETABORT && lqi == 0) {
1218 			err = ENODEV;
1219 		} else if (cmd == TTYMUX_GETABORT) {
1220 			if (lqi->sm_break_abort_on == 0 &&
1221 			    lqi->sm_ctrla_abort_on == 0) {
1222 				method = SOFTHARD_BREAK;
1223 				enable = 0;
1224 			} else {
1225 				enable = 1;
1226 				if (lqi->sm_break_abort_on == 0)
1227 					method = SOFTWARE_BREAK;
1228 				else if (lqi->sm_ctrla_abort_on == 0)
1229 					method = HARDWARE_BREAK;
1230 				else
1231 					method = SOFTHARD_BREAK;
1232 			}
1233 
1234 #ifdef _SYSCALL32_IMPL
1235 			if ((iobp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
1236 				abreq32->method = method;
1237 				abreq32->enable = (uint32_t)enable;
1238 			} else
1239 #endif
1240 			{
1241 				abreq->ttymux_method = method;
1242 				abreq->ttymux_enable = enable;
1243 			}
1244 		} else {
1245 			iobp->ioc_count = 0;
1246 			sm_dbg('I', ("lqi is 0x%p\n", lqi));
1247 			if (lqi == 0) {
1248 				if (method == HARDWARE_BREAK)
1249 					sm_ssp->sm_break_abort_on = enable;
1250 				else if (method == SOFTWARE_BREAK)
1251 					sm_ssp->sm_ctrla_abort_on = enable;
1252 				else if (method == SOFTHARD_BREAK) {
1253 					sm_ssp->sm_break_abort_on = enable;
1254 					sm_ssp->sm_ctrla_abort_on = enable;
1255 				} else {
1256 					sm_dbg('I', ("%d - invalid\n", method));
1257 					iobp->ioc_rval = -1;
1258 					return ((iobp->ioc_error = EINVAL));
1259 				}
1260 
1261 				if (sm_ssp->sm_lconsole) {
1262 					sm_dbg('I', ("lconsole 0x%p (0x%p)\n",
1263 					    sm_ssp->sm_lconsole,
1264 					    sm_ssp->sm_lconsole->sm_lqs));
1265 				} else {
1266 					sm_dbg('I', ("lconsole is null\n"));
1267 				}
1268 
1269 				lqi = (sm_ssp->sm_lconsole) ?
1270 				    sm_ssp->sm_lconsole->sm_lqs : 0;
1271 			}
1272 			while (lqi) {
1273 				if (method == HARDWARE_BREAK)
1274 					lqi->sm_break_abort_on = enable;
1275 				else if (method == SOFTWARE_BREAK)
1276 					lqi->sm_ctrla_abort_on = enable;
1277 				else if (method == SOFTHARD_BREAK) {
1278 					lqi->sm_break_abort_on = enable;
1279 					lqi->sm_ctrla_abort_on = enable;
1280 				} else {
1281 					sm_dbg('I', ("%d: invalid\n", method));
1282 					iobp->ioc_rval = -1;
1283 					return ((iobp->ioc_error = EINVAL));
1284 				}
1285 
1286 				lqi = (ldev == NODEV) ? lqi->sm_nlqi : 0;
1287 			}
1288 		}
1289 		iobp->ioc_rval = err ? -1 : 0;
1290 		iobp->ioc_error = err;
1291 		break;
1292 	default:
1293 		iobp->ioc_rval = -1;
1294 		iobp->ioc_error = EINVAL;
1295 	}
1296 	return (iobp->ioc_error);
1297 }
1298 
1299 /*
1300  * Process ioctls specific to the ttymux driver.
1301  */
1302 /*ARGSUSED*/
1303 int
1304 sm_ioctl_cmd(sm_uqi_t *uqi, mblk_t *mp)
1305 {
1306 	struct iocblk *iobp = (struct iocblk *)mp->b_rptr;
1307 
1308 	iobp->ioc_rval = 0;
1309 
1310 	/*
1311 	 * This routine does not support transparent ioctls
1312 	 */
1313 	if (iobp->ioc_count == TRANSPARENT) {
1314 		sm_dbg('Y', ("sm_ioctl_cmd: unsupported ioctl\n"));
1315 		iobp->ioc_error = ENOTSUP;
1316 		DB_TYPE(mp) = M_IOCNAK;
1317 		if (mp->b_cont)
1318 			freemsg(unlinkb(mp));
1319 		return (ENOTSUP);
1320 	}
1321 
1322 	switch (iobp->ioc_cmd) {
1323 	case TTYMUX_CONSDEV:
1324 		iobp->ioc_error = ttymux_console_ioctl(mp);
1325 		break;
1326 	case TTYMUX_ASSOC:
1327 	case TTYMUX_DISASSOC:
1328 		iobp->ioc_error = ttymux_link_ioctl(mp);
1329 		break;
1330 	case TTYMUX_GETLINK:
1331 		iobp->ioc_error = ttymux_query_link_ioctl(mp);
1332 		break;
1333 	case TTYMUX_LIST:
1334 		return (ttymux_query_links_ioctl(mp));
1335 	case TTYMUX_SETCTL:
1336 	case TTYMUX_GETCTL:
1337 		iobp->ioc_error = ENOTSUP;
1338 		break;
1339 	case TTYMUX_GETABORTSTR:
1340 	case TTYMUX_SETABORT:
1341 	case TTYMUX_GETABORT:
1342 		iobp->ioc_error = ttymux_abort_ioctl(mp);
1343 		break;
1344 	default:
1345 		iobp->ioc_error = EINVAL;
1346 		break;
1347 	}
1348 
1349 	DB_TYPE(mp) = iobp->ioc_error ? M_IOCNAK : M_IOCACK;
1350 
1351 	if ((iobp->ioc_error || iobp->ioc_count == 0) && mp->b_cont)
1352 	    freemsg(unlinkb(mp));
1353 
1354 	sm_dbg('I', ("TTYMUX IOCTL: err %d rval %d count %d\n",
1355 	    iobp->ioc_error, iobp->ioc_rval, iobp->ioc_count));
1356 
1357 	return (iobp->ioc_error);
1358 }
1359