stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 1 | /* |
| 2 | * CDDL HEADER START |
| 3 | * |
| 4 | * The contents of this file are subject to the terms of the |
lq150181 | fea9cb9 | 2006-01-12 18:17:46 -0800 | [diff] [blame] | 5 | * Common Development and Distribution License (the "License"). |
| 6 | * You may not use this file except in compliance with the License. |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 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 | */ |
lq150181 | fea9cb9 | 2006-01-12 18:17:46 -0800 | [diff] [blame] | 21 | |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 22 | /* |
rui zang - Sun Microsystems - Beijing China | ceeba6f | 2010-04-21 13:58:16 +0800 | [diff] [blame] | 23 | * Copyright (c) 1982, 2010, Oracle and/or its affiliates. All rights reserved. |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 24 | */ |
| 25 | |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 26 | /* |
| 27 | * Indirect console driver for Sun. |
| 28 | * |
| 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. |
| 33 | * |
| 34 | * rconsvp is set in autoconf.c:consconfig(), based on information obtained |
| 35 | * from the EEPROM. |
| 36 | * |
| 37 | * XXX: The driver still needs to be converted to use ANSI C consistently |
| 38 | * throughout. |
| 39 | */ |
| 40 | |
| 41 | #include <sys/types.h> |
| 42 | #include <sys/open.h> |
| 43 | #include <sys/param.h> |
| 44 | #include <sys/systm.h> |
| 45 | #include <sys/signal.h> |
| 46 | #include <sys/cred.h> |
| 47 | #include <sys/user.h> |
| 48 | #include <sys/proc.h> |
| 49 | #include <sys/disp.h> |
| 50 | #include <sys/file.h> |
| 51 | #include <sys/taskq.h> |
| 52 | #include <sys/log.h> |
| 53 | #include <sys/vnode.h> |
| 54 | #include <sys/uio.h> |
| 55 | #include <sys/stat.h> |
| 56 | |
| 57 | #include <sys/console.h> |
| 58 | #include <sys/consdev.h> |
| 59 | |
| 60 | #include <sys/stream.h> |
| 61 | #include <sys/strsubr.h> |
| 62 | #include <sys/poll.h> |
| 63 | |
| 64 | #include <sys/debug.h> |
| 65 | |
| 66 | #include <sys/conf.h> |
| 67 | #include <sys/ddi.h> |
| 68 | #include <sys/sunddi.h> |
rui zang - Sun Microsystems - Beijing China | ceeba6f | 2010-04-21 13:58:16 +0800 | [diff] [blame] | 69 | #include <sys/vt.h> |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 70 | |
| 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); |
| 80 | |
| 81 | static dev_info_t *cn_dip; /* private copy of devinfo pointer */ |
| 82 | |
| 83 | static struct cb_ops cn_cb_ops = { |
| 84 | |
| 85 | cnopen, /* open */ |
| 86 | cnclose, /* close */ |
| 87 | nodev, /* strategy */ |
| 88 | nodev, /* print */ |
| 89 | nodev, /* dump */ |
| 90 | cnread, /* read */ |
| 91 | cnwrite, /* write */ |
| 92 | cnioctl, /* ioctl */ |
| 93 | nodev, /* devmap */ |
| 94 | nodev, /* mmap */ |
| 95 | nodev, /* segmap */ |
| 96 | cnpoll, /* poll */ |
| 97 | ddi_prop_op, /* cb_prop_op */ |
| 98 | 0, /* streamtab */ |
| 99 | D_NEW | D_MP /* Driver compatibility flag */ |
| 100 | |
| 101 | }; |
| 102 | |
| 103 | static struct dev_ops cn_ops = { |
| 104 | |
| 105 | DEVO_REV, /* devo_rev, */ |
| 106 | 0, /* refcnt */ |
| 107 | cn_info, /* info */ |
| 108 | nulldev, /* identify */ |
| 109 | nulldev, /* probe */ |
| 110 | cn_attach, /* attach */ |
| 111 | cn_detach, /* detach */ |
| 112 | nodev, /* reset */ |
| 113 | &cn_cb_ops, /* driver operations */ |
Sherry Moore | 1939740 | 2008-09-22 16:30:26 -0700 | [diff] [blame] | 114 | (struct bus_ops *)0, /* bus operations */ |
| 115 | NULL, /* power */ |
| 116 | ddi_quiesce_not_needed, /* quiesce */ |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 117 | |
| 118 | }; |
| 119 | |
| 120 | /* |
| 121 | * Global variables associated with the console device: |
| 122 | * |
| 123 | * XXX: There are too many of these! |
amw | da6c28a | 2007-10-25 16:34:29 -0700 | [diff] [blame] | 124 | * moved to space.c to become resident in the kernel so that cons |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 125 | * can be loadable. |
| 126 | */ |
| 127 | |
| 128 | extern dev_t rconsdev; /* "hardware" console */ |
| 129 | extern vnode_t *rconsvp; /* pointer to vnode for that device */ |
| 130 | |
| 131 | /* |
| 132 | * XXX: consulted in prsubr.c, for /proc entry point for obtaining ps info. |
| 133 | */ |
| 134 | extern dev_t uconsdev; /* What the user thinks is the console device */ |
| 135 | |
| 136 | /* |
| 137 | * Private driver state: |
| 138 | */ |
| 139 | |
| 140 | /* |
| 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. |
| 149 | */ |
| 150 | static uint_t rconsopen; |
| 151 | |
| 152 | |
| 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> |
| 159 | |
| 160 | |
| 161 | extern int nodev(), nulldev(); |
| 162 | extern int dseekneg_flag; |
| 163 | extern struct mod_ops mod_driverops; |
| 164 | extern struct dev_ops cn_ops; |
| 165 | |
| 166 | /* |
| 167 | * Module linkage information for the kernel. |
| 168 | */ |
| 169 | |
| 170 | static struct modldrv modldrv = { |
| 171 | &mod_driverops, /* Type of module. This one is a pseudo driver */ |
Sherry Moore | 1939740 | 2008-09-22 16:30:26 -0700 | [diff] [blame] | 172 | "Console redirection driver", |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 173 | &cn_ops, /* driver ops */ |
| 174 | }; |
| 175 | |
| 176 | static struct modlinkage modlinkage = { |
| 177 | MODREV_1, |
| 178 | &modldrv, |
| 179 | NULL |
| 180 | }; |
| 181 | |
| 182 | int |
| 183 | _init(void) |
| 184 | { |
| 185 | return (mod_install(&modlinkage)); |
| 186 | } |
| 187 | |
| 188 | int |
| 189 | _fini(void) |
| 190 | { |
| 191 | return (EBUSY); |
| 192 | } |
| 193 | |
| 194 | int |
| 195 | _info(struct modinfo *modinfop) |
| 196 | { |
| 197 | return (mod_info(&modlinkage, modinfop)); |
| 198 | } |
| 199 | |
| 200 | /* |
| 201 | * DDI glue routines |
| 202 | */ |
| 203 | static int |
| 204 | cn_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) |
| 205 | { |
| 206 | if (cmd != DDI_ATTACH) |
| 207 | return (DDI_FAILURE); |
| 208 | |
| 209 | if (ddi_create_minor_node(devi, "syscon", S_IFCHR, |
| 210 | 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { |
| 211 | return (DDI_FAILURE); |
| 212 | } |
| 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); |
| 217 | } |
| 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); |
| 222 | } |
rui zang - Sun Microsystems - Beijing China | aecfc01 | 2008-09-25 14:01:48 +0800 | [diff] [blame] | 223 | |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 224 | cn_dip = devi; |
| 225 | return (DDI_SUCCESS); |
| 226 | } |
| 227 | |
| 228 | static int |
| 229 | cn_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) |
| 230 | { |
| 231 | if (cmd != DDI_DETACH) |
| 232 | return (DDI_FAILURE); |
| 233 | ddi_remove_minor_node(devi, NULL); |
| 234 | uconsdev = NODEV; |
| 235 | return (DDI_SUCCESS); |
| 236 | } |
| 237 | |
| 238 | /* ARGSUSED */ |
| 239 | static int |
| 240 | cn_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) |
| 241 | { |
| 242 | int error = DDI_FAILURE; |
| 243 | |
| 244 | switch (infocmd) { |
| 245 | case DDI_INFO_DEVT2DEVINFO: |
| 246 | if (getminor((dev_t)arg) == 0 && cn_dip != NULL) { |
| 247 | *result = (void *) cn_dip; |
| 248 | error = DDI_SUCCESS; |
| 249 | } |
| 250 | break; |
| 251 | |
| 252 | case DDI_INFO_DEVT2INSTANCE: |
| 253 | if (getminor((dev_t)arg) == 0) { |
| 254 | *result = (void *)0; |
| 255 | error = DDI_SUCCESS; |
| 256 | } |
| 257 | break; |
| 258 | |
| 259 | default: |
| 260 | break; |
| 261 | } |
| 262 | |
| 263 | return (error); |
| 264 | } |
| 265 | |
| 266 | /* |
| 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. |
| 271 | */ |
| 272 | |
| 273 | /* ARGSUSED */ |
| 274 | static int |
| 275 | cnopen(dev_t *dev, int flag, int state, struct cred *cred) |
| 276 | { |
| 277 | int err; |
| 278 | static int been_here; |
| 279 | vnode_t *vp = rconsvp; |
| 280 | |
| 281 | ASSERT(cred != NULL); |
| 282 | |
| 283 | if (rconsvp == NULL) |
| 284 | return (0); |
| 285 | |
James Anderson | 361ed64 | 2009-07-09 14:36:20 -0700 | [diff] [blame] | 286 | /* |
| 287 | * Enable virtual console I/O for console logging if needed. |
| 288 | */ |
| 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"); |
| 293 | } |
| 294 | } |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 295 | |
| 296 | /* |
| 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 |
| 301 | * this hook. |
| 302 | */ |
| 303 | if (vp->v_stream) { |
| 304 | str_cn_clean(vp); |
| 305 | } |
| 306 | |
| 307 | /* |
| 308 | * XXX: Set hook to tell /proc about underlying console. (There's |
| 309 | * gotta be a better way...) |
| 310 | */ |
| 311 | if (state != OTYP_CHR || getminor(*dev) != 0) |
| 312 | return (ENXIO); |
| 313 | if (been_here == 0) { |
| 314 | uconsdev = *dev; |
| 315 | been_here = 1; |
| 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); |
| 321 | } |
| 322 | |
amw | da6c28a | 2007-10-25 16:34:29 -0700 | [diff] [blame] | 323 | if ((err = VOP_OPEN(&vp, flag, cred, NULL)) != 0) |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 324 | return (err); |
| 325 | |
| 326 | /* |
| 327 | * The underlying driver is not allowed to have cloned itself |
| 328 | * for this open. |
| 329 | */ |
| 330 | if (vp != rconsvp) { |
| 331 | /* |
| 332 | * It might happen that someone set rconsvp to NULL |
| 333 | * whilst we were in the middle of the open. |
| 334 | */ |
| 335 | if (rconsvp == NULL) { |
amw | da6c28a | 2007-10-25 16:34:29 -0700 | [diff] [blame] | 336 | (void) VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 337 | return (0); |
| 338 | } |
| 339 | cmn_err(CE_PANIC, "cnopen: cloned open"); |
| 340 | } |
| 341 | |
| 342 | rconsopen++; |
| 343 | |
| 344 | return (0); |
| 345 | } |
| 346 | |
| 347 | /* ARGSUSED */ |
| 348 | static int |
| 349 | cnclose(dev_t dev, int flag, int state, struct cred *cred) |
| 350 | { |
| 351 | int err = 0; |
| 352 | vnode_t *vp; |
| 353 | |
| 354 | /* |
| 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.) |
| 359 | */ |
| 360 | if (state != OTYP_CHR) |
| 361 | return (ENXIO); |
| 362 | |
| 363 | if (rconsvp == NULL) |
| 364 | return (0); |
| 365 | |
| 366 | while ((rconsopen != 0) && ((vp = rconsvp) != NULL)) { |
amw | da6c28a | 2007-10-25 16:34:29 -0700 | [diff] [blame] | 367 | err = VOP_CLOSE(vp, flag, 1, (offset_t)0, cred, NULL); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 368 | if (!err) { |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 369 | rconsopen--; |
| 370 | } |
| 371 | } |
| 372 | return (err); |
| 373 | } |
| 374 | |
| 375 | /* ARGSUSED */ |
| 376 | static int |
| 377 | cnread(dev_t dev, struct uio *uio, struct cred *cred) |
| 378 | { |
| 379 | kcondvar_t sleep_forever; |
| 380 | kmutex_t sleep_forever_mutex; |
| 381 | |
| 382 | if (rconsvp == NULL) { |
| 383 | /* |
| 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 |
| 387 | * mode. |
| 388 | */ |
| 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); |
| 394 | return (EIO); |
| 395 | } |
| 396 | |
| 397 | if (rconsvp->v_stream != NULL) |
| 398 | return (strread(rconsvp, uio, cred)); |
| 399 | else |
| 400 | return (cdev_read(rconsdev, uio, cred)); |
| 401 | } |
| 402 | |
| 403 | /* ARGSUSED */ |
| 404 | static int |
| 405 | cnwrite(dev_t dev, struct uio *uio, struct cred *cred) |
| 406 | { |
| 407 | if (rconsvp == NULL) { |
| 408 | uio->uio_resid = 0; |
| 409 | return (0); |
| 410 | } |
| 411 | |
James Anderson | 361ed64 | 2009-07-09 14:36:20 -0700 | [diff] [blame] | 412 | /* |
| 413 | * Output to virtual console for logging if enabled. |
| 414 | */ |
| 415 | if (vsconsvp != NULL && vsconsvp->v_stream != NULL) { |
| 416 | struiod_t uiod; |
| 417 | |
| 418 | /* |
| 419 | * strwrite modifies uio so need to make copy. |
| 420 | */ |
| 421 | (void) uiodup(uio, &uiod.d_uio, uiod.d_iov, |
| 422 | sizeof (uiod.d_iov) / sizeof (*uiod.d_iov)); |
| 423 | |
| 424 | (void) strwrite(vsconsvp, &uiod.d_uio, cred); |
| 425 | } |
| 426 | |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 427 | if (rconsvp->v_stream != NULL) |
| 428 | return (strwrite(rconsvp, uio, cred)); |
| 429 | else |
| 430 | return (cdev_write(rconsdev, uio, cred)); |
| 431 | } |
| 432 | |
| 433 | /* ARGSUSED */ |
| 434 | static int |
lq150181 | fea9cb9 | 2006-01-12 18:17:46 -0800 | [diff] [blame] | 435 | cnprivateioc(dev_t dev, int cmd, intptr_t arg, int flag, struct cred *cred, |
| 436 | int *rvalp) |
| 437 | { |
| 438 | |
| 439 | /* currently we only support one ioctl */ |
| 440 | if (cmd != CONS_GETTERM) |
| 441 | return (EINVAL); |
| 442 | |
| 443 | /* Confirm iwscn is immediate target of cn redirection */ |
| 444 | if (rconsvp != wsconsvp) |
| 445 | return (ENODEV); |
| 446 | |
| 447 | /* |
| 448 | * If the redirection client is not wc, it should return |
| 449 | * error upon receiving the CONS_GETTERM ioctl. |
| 450 | * |
| 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. |
| 454 | */ |
| 455 | return (cdev_ioctl(rconsdev, CONS_GETTERM, arg, flag, cred, rvalp)); |
| 456 | } |
| 457 | |
| 458 | /* ARGSUSED */ |
| 459 | static int |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 460 | cnioctl(dev_t dev, int cmd, intptr_t arg, int flag, struct cred *cred, |
| 461 | int *rvalp) |
| 462 | { |
| 463 | if (rconsvp == NULL) |
| 464 | return (0); |
| 465 | |
rui zang - Sun Microsystems - Beijing China | ceeba6f | 2010-04-21 13:58:16 +0800 | [diff] [blame] | 466 | /* |
| 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 |
| 471 | * ioctl. |
| 472 | */ |
| 473 | if (cmd == VT_SET_CONSUSER) |
| 474 | cmd = VT_RESET_CONSUSER; |
| 475 | |
lq150181 | fea9cb9 | 2006-01-12 18:17:46 -0800 | [diff] [blame] | 476 | if ((cmd & _CNIOC_MASK) == _CNIOC) |
| 477 | return (cnprivateioc(dev, cmd, arg, flag, cred, rvalp)); |
rui zang - Sun Microsystems - Beijing China | ceeba6f | 2010-04-21 13:58:16 +0800 | [diff] [blame] | 478 | |
| 479 | if (rconsvp->v_stream != NULL) |
| 480 | return (strioctl(rconsvp, cmd, arg, flag, U_TO_K, |
| 481 | cred, rvalp)); |
| 482 | |
| 483 | return (cdev_ioctl(rconsdev, cmd, arg, flag, cred, rvalp)); |
stevel@tonic-gate | 7c478bd | 2005-06-14 00:00:00 -0700 | [diff] [blame] | 484 | } |
| 485 | |
| 486 | /* ARGSUSED */ |
| 487 | static int |
| 488 | cnpoll(dev_t dev, short events, int anyyet, short *reventsp, |
| 489 | struct pollhead **phpp) |
| 490 | { |
| 491 | if (rconsvp == NULL) |
| 492 | return (nochpoll(dev, events, anyyet, reventsp, phpp)); |
| 493 | |
| 494 | if (rconsvp->v_stream != NULL) |
| 495 | return (strpoll(rconsvp->v_stream, events, anyyet, reventsp, |
| 496 | phpp)); |
| 497 | else |
| 498 | return (cdev_poll(rconsdev, events, anyyet, reventsp, phpp)); |
| 499 | } |