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.
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.
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]
23 * Copyright (c) 1982, 2010, Oracle and/or its affiliates. All rights reserved.
27 * Indirect console driver for Sun.
29 * Redirects all I/O to the device designated as the underlying "hardware"
30 * console, as given by the value of rconsvp. The implementation assumes that
31 * rconsvp denotes a STREAMS device; the assumption is justified since
32 * consoles must be capable of effecting tty semantics.
34 * rconsvp is set in autoconf.c:consconfig(), based on information obtained
37 * XXX: The driver still needs to be converted to use ANSI C consistently
41 #include <sys/types.h>
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/signal.h>
51 #include <sys/taskq.h>
53 #include <sys/vnode.h>
57 #include <sys/console.h>
58 #include <sys/consdev.h>
60 #include <sys/stream.h>
61 #include <sys/strsubr.h>
64 #include <sys/debug.h>
68 #include <sys/sunddi.h>
71 static int cnopen(dev_t
*, int, int, struct cred
*);
72 static int cnclose(dev_t
, int, int, struct cred
*);
73 static int cnread(dev_t
, struct uio
*, struct cred
*);
74 static int cnwrite(dev_t
, struct uio
*, struct cred
*);
75 static int cnioctl(dev_t
, int, intptr_t, int, struct cred
*, int *);
76 static int cnpoll(dev_t
, short, int, short *, struct pollhead
**);
77 static int cn_info(dev_info_t
*, ddi_info_cmd_t
, void *, void **);
78 static int cn_attach(dev_info_t
*, ddi_attach_cmd_t
);
79 static int cn_detach(dev_info_t
*, ddi_detach_cmd_t
);
81 static dev_info_t
*cn_dip
; /* private copy of devinfo pointer */
83 static struct cb_ops cn_cb_ops
= {
97 ddi_prop_op
, /* cb_prop_op */
99 D_NEW
| D_MP
/* Driver compatibility flag */
103 static struct dev_ops cn_ops
= {
105 DEVO_REV
, /* devo_rev, */
108 nulldev
, /* identify */
110 cn_attach
, /* attach */
111 cn_detach
, /* detach */
113 &cn_cb_ops
, /* driver operations */
114 (struct bus_ops
*)0, /* bus operations */
116 ddi_quiesce_not_needed
, /* quiesce */
121 * Global variables associated with the console device:
123 * XXX: There are too many of these!
124 * moved to space.c to become resident in the kernel so that cons
128 extern dev_t rconsdev
; /* "hardware" console */
129 extern vnode_t
*rconsvp
; /* pointer to vnode for that device */
132 * XXX: consulted in prsubr.c, for /proc entry point for obtaining ps info.
134 extern dev_t uconsdev
; /* What the user thinks is the console device */
137 * Private driver state:
141 * The underlying console device potentially can be opened through (at least)
142 * two paths: through this driver and through the underlying device's driver.
143 * To ensure that reference counts are meaningful and therefore that close
144 * routines are called at the right time, it's important to make sure that
145 * rconsvp's s_count field (i.e., the count on the underlying device) never
146 * has a contribution of more than one through this driver, regardless of how
147 * many times this driver's been opened. rconsopen keeps track of the
148 * necessary information to ensure this property.
150 static uint_t rconsopen
;
153 #include <sys/types.h>
154 #include <sys/conf.h>
155 #include <sys/param.h>
156 #include <sys/systm.h>
157 #include <sys/errno.h>
158 #include <sys/modctl.h>
161 extern int nodev(), nulldev();
162 extern int dseekneg_flag
;
163 extern struct mod_ops mod_driverops
;
164 extern struct dev_ops cn_ops
;
167 * Module linkage information for the kernel.
170 static struct modldrv modldrv
= {
171 &mod_driverops
, /* Type of module. This one is a pseudo driver */
172 "Console redirection driver",
173 &cn_ops
, /* driver ops */
176 static struct modlinkage modlinkage
= {
185 return (mod_install(&modlinkage
));
195 _info(struct modinfo
*modinfop
)
197 return (mod_info(&modlinkage
, modinfop
));
204 cn_attach(dev_info_t
*devi
, ddi_attach_cmd_t cmd
)
206 if (cmd
!= DDI_ATTACH
)
207 return (DDI_FAILURE
);
209 if (ddi_create_minor_node(devi
, "syscon", S_IFCHR
,
210 0, DDI_PSEUDO
, NULL
) == DDI_FAILURE
) {
211 return (DDI_FAILURE
);
213 if (ddi_create_minor_node(devi
, "systty", S_IFCHR
,
214 0, DDI_PSEUDO
, NULL
) == DDI_FAILURE
) {
215 ddi_remove_minor_node(devi
, NULL
);
216 return (DDI_FAILURE
);
218 if (ddi_create_minor_node(devi
, "console", S_IFCHR
,
219 0, DDI_PSEUDO
, NULL
) == DDI_FAILURE
) {
220 ddi_remove_minor_node(devi
, NULL
);
221 return (DDI_FAILURE
);
225 return (DDI_SUCCESS
);
229 cn_detach(dev_info_t
*devi
, ddi_detach_cmd_t cmd
)
231 if (cmd
!= DDI_DETACH
)
232 return (DDI_FAILURE
);
233 ddi_remove_minor_node(devi
, NULL
);
235 return (DDI_SUCCESS
);
240 cn_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
242 int error
= DDI_FAILURE
;
245 case DDI_INFO_DEVT2DEVINFO
:
246 if (getminor((dev_t
)arg
) == 0 && cn_dip
!= NULL
) {
247 *result
= (void *) cn_dip
;
252 case DDI_INFO_DEVT2INSTANCE
:
253 if (getminor((dev_t
)arg
) == 0) {
267 * XXX Caution: before allowing more than 256 minor devices on the
268 * console, make sure you understand the 'compatibility' hack
269 * in ufs_iget() that translates old dev_t's to new dev_t's.
270 * See bugid 1098104 for the sordid details.
275 cnopen(dev_t
*dev
, int flag
, int state
, struct cred
*cred
)
278 static int been_here
;
279 vnode_t
*vp
= rconsvp
;
281 ASSERT(cred
!= NULL
);
287 * Enable virtual console I/O for console logging if needed.
289 if (vsconsvp
!= NULL
&& vsconsvp
->v_stream
== NULL
) {
290 if (VOP_OPEN(&vsconsvp
, FREAD
| FWRITE
, cred
, NULL
) != 0) {
291 cmn_err(CE_WARN
, "cnopen: failed to open vsconsvp "
292 "for virtual console logging");
297 * XXX: Clean up inactive PIDs from previous opens if any.
298 * These would have been created as a result of an I_SETSIG
299 * issued against console. This is a workaround, and
300 * console driver must be correctly redesigned not to need
308 * XXX: Set hook to tell /proc about underlying console. (There's
309 * gotta be a better way...)
311 if (state
!= OTYP_CHR
|| getminor(*dev
) != 0)
313 if (been_here
== 0) {
316 if (vn_open("/dev/console", UIO_SYSSPACE
, FWRITE
| FNOCTTY
,
317 0, &console_vnode
, 0, 0) == 0)
318 console_taskq
= taskq_create("console_taskq",
319 1, maxclsyspri
- 1, LOG_LOWAT
/ LOG_MSGSIZE
,
320 LOG_HIWAT
/ LOG_MSGSIZE
, TASKQ_PREPOPULATE
);
323 if ((err
= VOP_OPEN(&vp
, flag
, cred
, NULL
)) != 0)
327 * The underlying driver is not allowed to have cloned itself
332 * It might happen that someone set rconsvp to NULL
333 * whilst we were in the middle of the open.
335 if (rconsvp
== NULL
) {
336 (void) VOP_CLOSE(vp
, flag
, 1, (offset_t
)0, cred
, NULL
);
339 cmn_err(CE_PANIC
, "cnopen: cloned open");
349 cnclose(dev_t dev
, int flag
, int state
, struct cred
*cred
)
355 * Since this is the _last_ close, it's our last chance to close the
356 * underlying device. (Note that if someone else has the underlying
357 * hardware console device open, we won't get here, since spec_close
358 * will see s_count > 1.)
360 if (state
!= OTYP_CHR
)
366 while ((rconsopen
!= 0) && ((vp
= rconsvp
) != NULL
)) {
367 err
= VOP_CLOSE(vp
, flag
, 1, (offset_t
)0, cred
, NULL
);
377 cnread(dev_t dev
, struct uio
*uio
, struct cred
*cred
)
379 kcondvar_t sleep_forever
;
380 kmutex_t sleep_forever_mutex
;
382 if (rconsvp
== NULL
) {
384 * Go to sleep forever. This seems like the least
385 * harmful thing to do if there's no console.
386 * EOF might be better if we're ending up single-user
389 cv_init(&sleep_forever
, NULL
, CV_DRIVER
, NULL
);
390 mutex_init(&sleep_forever_mutex
, NULL
, MUTEX_DRIVER
, NULL
);
391 mutex_enter(&sleep_forever_mutex
);
392 (void) cv_wait_sig(&sleep_forever
, &sleep_forever_mutex
);
393 mutex_exit(&sleep_forever_mutex
);
397 if (rconsvp
->v_stream
!= NULL
)
398 return (strread(rconsvp
, uio
, cred
));
400 return (cdev_read(rconsdev
, uio
, cred
));
405 cnwrite(dev_t dev
, struct uio
*uio
, struct cred
*cred
)
407 if (rconsvp
== NULL
) {
413 * Output to virtual console for logging if enabled.
415 if (vsconsvp
!= NULL
&& vsconsvp
->v_stream
!= NULL
) {
419 * strwrite modifies uio so need to make copy.
421 (void) uiodup(uio
, &uiod
.d_uio
, uiod
.d_iov
,
422 sizeof (uiod
.d_iov
) / sizeof (*uiod
.d_iov
));
424 (void) strwrite(vsconsvp
, &uiod
.d_uio
, cred
);
427 if (rconsvp
->v_stream
!= NULL
)
428 return (strwrite(rconsvp
, uio
, cred
));
430 return (cdev_write(rconsdev
, uio
, cred
));
435 cnprivateioc(dev_t dev
, int cmd
, intptr_t arg
, int flag
, struct cred
*cred
,
439 /* currently we only support one ioctl */
440 if (cmd
!= CONS_GETTERM
)
443 /* Confirm iwscn is immediate target of cn redirection */
444 if (rconsvp
!= wsconsvp
)
448 * If the redirection client is not wc, it should return
449 * error upon receiving the CONS_GETTERM ioctl.
451 * if it is wc, we know that the target supports the CONS_GETTERM
452 * ioctl, which very conviently has the exact same data
453 * format as this ioctl... so let's just pass it on.
455 return (cdev_ioctl(rconsdev
, CONS_GETTERM
, arg
, flag
, cred
, rvalp
));
460 cnioctl(dev_t dev
, int cmd
, intptr_t arg
, int flag
, struct cred
*cred
,
467 * In wc, VT_SET_CONSUSER which comes from minor node 0
468 * has two sources -- either /dev/console or /dev/vt/0 .
469 * We need a way to differentiate them, so here we
470 * change VT_SET_CONSUSER to a private VT_RESET_CONSUSER
473 if (cmd
== VT_SET_CONSUSER
)
474 cmd
= VT_RESET_CONSUSER
;
476 if ((cmd
& _CNIOC_MASK
) == _CNIOC
)
477 return (cnprivateioc(dev
, cmd
, arg
, flag
, cred
, rvalp
));
479 if (rconsvp
->v_stream
!= NULL
)
480 return (strioctl(rconsvp
, cmd
, arg
, flag
, U_TO_K
,
483 return (cdev_ioctl(rconsdev
, cmd
, arg
, flag
, cred
, rvalp
));
488 cnpoll(dev_t dev
, short events
, int anyyet
, short *reventsp
,
489 struct pollhead
**phpp
)
492 return (nochpoll(dev
, events
, anyyet
, reventsp
, phpp
));
494 if (rconsvp
->v_stream
!= NULL
)
495 return (strpoll(rconsvp
->v_stream
, events
, anyyet
, reventsp
,
498 return (cdev_poll(rconsdev
, events
, anyyet
, reventsp
, phpp
));