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 1990-1992,1996,1998-2003 Sun Microsystems, Inc. 24 * All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 29 /* All Rights Reserved */ 30 31 32 #pragma ident "%Z%%M% %I% %E% SMI" 33 /* from S5R4 1.22 */ 34 35 /* 36 * Indirect driver for controlling tty. 37 */ 38 #include <sys/types.h> 39 #include <sys/errno.h> 40 #include <sys/conf.h> 41 #include <sys/proc.h> 42 #include <sys/tty.h> 43 #include <sys/stream.h> 44 #include <sys/strsubr.h> 45 #include <sys/cred.h> 46 #include <sys/uio.h> 47 #include <sys/session.h> 48 #include <sys/ddi.h> 49 #include <sys/debug.h> 50 #include <sys/stat.h> 51 #include <sys/sunddi.h> 52 #include <sys/param.h> 53 #include <sys/systm.h> 54 #include <sys/modctl.h> 55 #include <sys/fs/snode.h> 56 #include <sys/file.h> 57 58 #define IS_STREAM(dev) (devopsp[getmajor(dev)]->devo_cb_ops->cb_str != NULL) 59 60 int syopen(dev_t *, int, int, cred_t *); 61 int syclose(dev_t, int, int, cred_t *); 62 int syread(dev_t, struct uio *, cred_t *); 63 int sywrite(dev_t, struct uio *, cred_t *); 64 int sypoll(dev_t, short, int, short *, struct pollhead **); 65 int syioctl(dev_t, int, intptr_t, int, cred_t *, int *); 66 67 static int sy_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 68 static int sy_attach(dev_info_t *, ddi_attach_cmd_t); 69 static dev_info_t *sy_dip; /* private copy of devinfo pointer */ 70 71 struct cb_ops sy_cb_ops = { 72 73 syopen, /* open */ 74 syclose, /* close */ 75 nodev, /* strategy */ 76 nodev, /* print */ 77 nodev, /* dump */ 78 syread, /* read */ 79 sywrite, /* write */ 80 syioctl, /* ioctl */ 81 nodev, /* devmap */ 82 nodev, /* mmap */ 83 nodev, /* segmap */ 84 sypoll, /* poll */ 85 ddi_prop_op, /* cb_prop_op */ 86 0, /* streamtab */ 87 D_NEW | D_MP /* Driver compatibility flag */ 88 89 }; 90 91 struct dev_ops sy_ops = { 92 93 DEVO_REV, /* devo_rev, */ 94 0, /* refcnt */ 95 sy_info, /* info */ 96 nulldev, /* identify */ 97 nulldev, /* probe */ 98 sy_attach, /* attach */ 99 nodev, /* detach */ 100 nodev, /* reset */ 101 &sy_cb_ops, /* driver operations */ 102 (struct bus_ops *)0 /* bus operations */ 103 104 }; 105 106 107 extern int nodev(void); 108 extern int nulldev(void); 109 extern int dseekneg_flag; 110 extern struct mod_ops mod_driverops; 111 extern struct dev_ops sy_ops; 112 113 /* 114 * Module linkage information for the kernel. 115 */ 116 117 static struct modldrv modldrv = { 118 &mod_driverops, /* Type of module. This one is a pseudo driver */ 119 "Indirect driver for tty 'sy' %I%", 120 &sy_ops, /* driver ops */ 121 }; 122 123 static struct modlinkage modlinkage = { 124 MODREV_1, 125 &modldrv, 126 NULL 127 }; 128 129 130 int 131 _init(void) 132 { 133 return (mod_install(&modlinkage)); 134 } 135 136 137 int 138 _fini(void) 139 { 140 return (mod_remove(&modlinkage)); 141 } 142 143 int 144 _info(struct modinfo *modinfop) 145 { 146 return (mod_info(&modlinkage, modinfop)); 147 } 148 149 /* ARGSUSED */ 150 static int 151 sy_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 152 { 153 if (ddi_create_minor_node(devi, "tty", S_IFCHR, 154 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { 155 ddi_remove_minor_node(devi, NULL); 156 return (-1); 157 } 158 sy_dip = devi; 159 return (DDI_SUCCESS); 160 } 161 162 /* ARGSUSED */ 163 static int 164 sy_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 165 { 166 dev_t dev = (dev_t)arg; 167 int error; 168 169 switch (infocmd) { 170 case DDI_INFO_DEVT2DEVINFO: 171 if (sy_dip == NULL) { 172 *result = (void *)NULL; 173 error = DDI_FAILURE; 174 } else { 175 *result = (void *) sy_dip; 176 error = DDI_SUCCESS; 177 } 178 break; 179 case DDI_INFO_DEVT2INSTANCE: 180 if (getminor(dev) != 0) { 181 *result = (void *)-1; 182 error = DDI_FAILURE; 183 } else { 184 *result = (void *)0; 185 error = DDI_SUCCESS; 186 } 187 break; 188 default: 189 error = DDI_FAILURE; 190 } 191 return (error); 192 } 193 194 195 /* ARGSUSED */ 196 int 197 syopen(dev_t *devp, int flag, int otyp, struct cred *cr) 198 { 199 dev_t ttyd; 200 vnode_t *ttyvp; 201 sess_t *sp = curproc->p_sessp; 202 int error; 203 204 if ((ttyd = sp->s_dev) == NODEV) 205 return (ENXIO); 206 TTY_HOLD(sp); 207 if ((ttyvp = sp->s_vp) == NULL) { 208 TTY_RELE(sp); 209 return (EIO); 210 } 211 212 /* 213 * Open the control terminal. The control terminal may be 214 * opened multiple times and it is closed in freectty(). 215 * The multi-open, single-clone means that no cloning 216 * can happen via this open, hence the assertion. 217 */ 218 error = VOP_OPEN(&ttyvp, FNOCTTY | flag, cr); 219 if (error == 0) { 220 struct snode *csp; 221 222 /* 223 * XXX: This driver binds a single minor number to the 224 * current controlling tty of the process issueing the 225 * open / close. If we implement a traditional close 226 * for this driver then specfs will only invoke this driver 227 * on the last close of our one minor number - which is not 228 * what we want. Since we already get the open / close 229 * semantic that we want from makectty and freectty, we reach 230 * back into the common snode and decrease the open count so 231 * that the specfs filtering of all but the last close 232 * does not get in our way. To clean this up, a new cb_flag 233 * that causes specfs to call the driver on each close 234 * should be considered. 235 */ 236 ASSERT(ttyd == ttyvp->v_rdev); 237 ASSERT(vn_matchops(ttyvp, spec_getvnodeops())); 238 csp = VTOS(VTOS(ttyvp)->s_commonvp); 239 mutex_enter(&csp->s_lock); 240 csp->s_count--; 241 mutex_exit(&csp->s_lock); 242 } 243 TTY_RELE(sp); 244 return (error); 245 } 246 247 /* ARGSUSED */ 248 int 249 syclose(dev_t dev, int flag, int otyp, struct cred *cr) 250 { 251 return (0); 252 } 253 254 /* ARGSUSED */ 255 int 256 syread(dev_t dev, struct uio *uiop, struct cred *cr) 257 { 258 vnode_t *ttyvp; 259 sess_t *sp = curproc->p_sessp; 260 int error; 261 262 if (sp->s_dev == NODEV) 263 return (ENXIO); 264 TTY_HOLD(sp); 265 if ((ttyvp = sp->s_vp) == NULL) { 266 TTY_RELE(sp); 267 return (EIO); 268 } 269 error = VOP_READ(ttyvp, uiop, 0, cr, NULL); 270 TTY_RELE(sp); 271 return (error); 272 273 } 274 275 /* ARGSUSED */ 276 int 277 sywrite(dev_t dev, struct uio *uiop, struct cred *cr) 278 { 279 vnode_t *ttyvp; 280 sess_t *sp = curproc->p_sessp; 281 int error; 282 283 if (sp->s_dev == NODEV) 284 return (ENXIO); 285 TTY_HOLD(sp); 286 if ((ttyvp = sp->s_vp) == NULL) { 287 TTY_RELE(sp); 288 return (EIO); 289 } 290 291 error = VOP_WRITE(ttyvp, uiop, 0, cr, NULL); 292 TTY_RELE(sp); 293 return (error); 294 } 295 296 297 /* ARGSUSED */ 298 int 299 syioctl(dev_t dev, int cmd, intptr_t arg, int mode, struct cred *cr, 300 int *rvalp) 301 { 302 vnode_t *ttyvp; 303 sess_t *sp = curproc->p_sessp; 304 int error; 305 306 if (sp->s_dev == NODEV) 307 return (ENXIO); 308 TTY_HOLD(sp); 309 if ((ttyvp = sp->s_vp) == NULL) { 310 TTY_RELE(sp); 311 return (EIO); 312 } 313 error = VOP_IOCTL(ttyvp, cmd, arg, mode, cr, rvalp); 314 TTY_RELE(sp); 315 return (error); 316 } 317 318 319 320 /* ARGSUSED */ 321 int 322 sypoll(dev_t dev, short events, int anyyet, short *reventsp, 323 struct pollhead **phpp) 324 { 325 vnode_t *ttyvp; 326 sess_t *sp = curproc->p_sessp; 327 int error; 328 329 if (sp->s_dev == NODEV) 330 return (ENXIO); 331 TTY_HOLD(sp); 332 if ((ttyvp = sp->s_vp) == NULL) { 333 TTY_RELE(sp); 334 return (EIO); 335 } 336 error = VOP_POLL(ttyvp, events, anyyet, reventsp, phpp); 337 TTY_RELE(sp); 338 return (error); 339 } 340