1 /* $NetBSD: tty_conf.c,v 1.54 2007/11/07 15:56:22 ad Exp $ */ 2 3 /*- 4 * Copyright (c) 2005, 2007 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /*- 40 * Copyright (c) 1982, 1986, 1991, 1993 41 * The Regents of the University of California. All rights reserved. 42 * (c) UNIX System Laboratories, Inc. 43 * All or some portions of this file are derived from material licensed 44 * to the University of California by American Telephone and Telegraph 45 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 46 * the permission of UNIX System Laboratories, Inc. 47 * 48 * Redistribution and use in source and binary forms, with or without 49 * modification, are permitted provided that the following conditions 50 * are met: 51 * 1. Redistributions of source code must retain the above copyright 52 * notice, this list of conditions and the following disclaimer. 53 * 2. Redistributions in binary form must reproduce the above copyright 54 * notice, this list of conditions and the following disclaimer in the 55 * documentation and/or other materials provided with the distribution. 56 * 3. Neither the name of the University nor the names of its contributors 57 * may be used to endorse or promote products derived from this software 58 * without specific prior written permission. 59 * 60 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 61 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 62 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 63 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 64 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 65 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 66 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 67 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 68 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 69 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 70 * SUCH DAMAGE. 71 * 72 * @(#)tty_conf.c 8.5 (Berkeley) 1/9/95 73 */ 74 75 #include <sys/cdefs.h> 76 __KERNEL_RCSID(0, "$NetBSD: tty_conf.c,v 1.54 2007/11/07 15:56:22 ad Exp $"); 77 78 #include <sys/param.h> 79 #include <sys/systm.h> 80 #include <sys/poll.h> 81 #include <sys/proc.h> 82 #include <sys/tty.h> 83 #include <sys/ttycom.h> 84 #include <sys/conf.h> 85 #include <sys/mutex.h> 86 #include <sys/queue.h> 87 88 static struct linesw termios_disc = { 89 .l_name = "termios", 90 .l_open = ttylopen, 91 .l_close = ttylclose, 92 .l_read = ttread, 93 .l_write = ttwrite, 94 .l_ioctl = ttynullioctl, 95 .l_rint = ttyinput, 96 .l_start = ttstart, 97 .l_modem = ttymodem, 98 .l_poll = ttpoll 99 }; 100 101 /* 102 * This is for the benefit of old BSD TTY compatbility, but since it is 103 * identical to termios (except for the name), don't bother conditionalizing 104 * it. 105 */ 106 static struct linesw ntty_disc = { /* old NTTYDISC */ 107 .l_name = "ntty", 108 .l_open = ttylopen, 109 .l_close = ttylclose, 110 .l_read = ttread, 111 .l_write = ttwrite, 112 .l_ioctl = ttynullioctl, 113 .l_rint = ttyinput, 114 .l_start = ttstart, 115 .l_modem = ttymodem, 116 .l_poll = ttpoll 117 }; 118 119 static LIST_HEAD(, linesw) ttyldisc_list = LIST_HEAD_INITIALIZER(ttyldisc_head); 120 121 /* 122 * Note: We don't bother refcounting termios_disc and ntty_disc; they can't 123 * be removed from the list, and termios_disc is likely to have very many 124 * references (could we overflow the count?). 125 */ 126 #define TTYLDISC_ISSTATIC(disc) \ 127 ((disc) == &termios_disc || (disc) == &ntty_disc) 128 129 #define TTYLDISC_HOLD(disc) \ 130 do { \ 131 if (! TTYLDISC_ISSTATIC(disc)) { \ 132 KASSERT((disc)->l_refcnt != UINT_MAX); \ 133 (disc)->l_refcnt++; \ 134 } \ 135 } while (/*CONSTCOND*/0) 136 137 #define TTYLDISC_RELE(disc) \ 138 do { \ 139 if (! TTYLDISC_ISSTATIC(disc)) { \ 140 KASSERT((disc)->l_refcnt != 0); \ 141 (disc)->l_refcnt--; \ 142 } \ 143 } while (/*CONSTCOND*/0) 144 145 #define TTYLDISC_ISINUSE(disc) \ 146 (TTYLDISC_ISSTATIC(disc) || (disc)->l_refcnt != 0) 147 148 /* 149 * Do nothing specific version of line 150 * discipline specific ioctl command. 151 */ 152 /*ARGSUSED*/ 153 int 154 ttynullioctl(struct tty *tp, u_long cmd, void *data, int flags, struct lwp *l) 155 { 156 157 return (EPASSTHROUGH); 158 } 159 160 /* 161 * Return error to line discipline 162 * specific poll call. 163 */ 164 /*ARGSUSED*/ 165 int 166 ttyerrpoll(struct tty *tp, int events, struct lwp *l) 167 { 168 169 return (POLLERR); 170 } 171 172 void 173 ttyldisc_init(void) 174 { 175 176 if (ttyldisc_attach(&termios_disc) != 0) 177 panic("ttyldisc_init: termios_disc"); 178 if (ttyldisc_attach(&ntty_disc) != 0) 179 panic("ttyldisc_init: ntty_disc"); 180 } 181 182 static struct linesw * 183 ttyldisc_lookup_locked(const char *name) 184 { 185 struct linesw *disc; 186 187 LIST_FOREACH(disc, &ttyldisc_list, l_list) { 188 if (strcmp(name, disc->l_name) == 0) 189 return (disc); 190 } 191 192 return (NULL); 193 } 194 195 /* 196 * Look up a line discipline by its name. Caller holds a reference on 197 * the returned line discipline. 198 */ 199 struct linesw * 200 ttyldisc_lookup(const char *name) 201 { 202 struct linesw *disc; 203 204 mutex_spin_enter(&tty_lock); 205 disc = ttyldisc_lookup_locked(name); 206 if (disc != NULL) 207 TTYLDISC_HOLD(disc); 208 mutex_spin_exit(&tty_lock); 209 210 return (disc); 211 } 212 213 /* 214 * Look up a line discipline by its legacy number. Caller holds a 215 * reference on the returned line discipline. 216 */ 217 struct linesw * 218 ttyldisc_lookup_bynum(int num) 219 { 220 struct linesw *disc; 221 222 mutex_spin_enter(&tty_lock); 223 224 LIST_FOREACH(disc, &ttyldisc_list, l_list) { 225 if (disc->l_no == num) { 226 TTYLDISC_HOLD(disc); 227 mutex_spin_exit(&tty_lock); 228 return (disc); 229 } 230 } 231 232 mutex_spin_exit(&tty_lock); 233 return (NULL); 234 } 235 236 /* 237 * Release a reference on a line discipline previously added by 238 * ttyldisc_lookup() or ttyldisc_lookup_bynum(). 239 */ 240 void 241 ttyldisc_release(struct linesw *disc) 242 { 243 244 if (disc == NULL) 245 return; 246 247 mutex_spin_enter(&tty_lock); 248 TTYLDISC_RELE(disc); 249 mutex_spin_exit(&tty_lock); 250 } 251 252 #define TTYLDISC_LEGACY_NUMBER_MIN 10 253 #define TTYLDISC_LEGACY_NUMBER_MAX INT_MAX 254 255 static void 256 ttyldisc_assign_legacy_number(struct linesw *disc) 257 { 258 static const struct { 259 const char *name; 260 int num; 261 } table[] = { 262 { "termios", TTYDISC }, 263 { "ntty", 2 /* XXX old NTTYDISC */ }, 264 { "tablet", TABLDISC }, 265 { "slip", SLIPDISC }, 266 { "ppp", PPPDISC }, 267 { "strip", STRIPDISC }, 268 { "hdlc", HDLCDISC }, 269 { NULL, 0 } 270 }; 271 struct linesw *ldisc; 272 int i; 273 274 for (i = 0; table[i].name != NULL; i++) { 275 if (strcmp(disc->l_name, table[i].name) == 0) { 276 disc->l_no = table[i].num; 277 return; 278 } 279 } 280 281 disc->l_no = TTYLDISC_LEGACY_NUMBER_MIN; 282 283 LIST_FOREACH(ldisc, &ttyldisc_list, l_list) { 284 if (disc->l_no == ldisc->l_no) { 285 KASSERT(disc->l_no < TTYLDISC_LEGACY_NUMBER_MAX); 286 disc->l_no++; 287 } 288 } 289 } 290 291 /* 292 * Register a line discipline. 293 */ 294 int 295 ttyldisc_attach(struct linesw *disc) 296 { 297 298 KASSERT(disc->l_name != NULL); 299 KASSERT(disc->l_open != NULL); 300 KASSERT(disc->l_close != NULL); 301 KASSERT(disc->l_read != NULL); 302 KASSERT(disc->l_write != NULL); 303 KASSERT(disc->l_ioctl != NULL); 304 KASSERT(disc->l_rint != NULL); 305 KASSERT(disc->l_start != NULL); 306 KASSERT(disc->l_modem != NULL); 307 KASSERT(disc->l_poll != NULL); 308 309 /* You are not allowed to exceed TTLINEDNAMELEN */ 310 if (strlen(disc->l_name) >= TTLINEDNAMELEN) 311 return (ENAMETOOLONG); 312 313 mutex_spin_enter(&tty_lock); 314 315 if (ttyldisc_lookup_locked(disc->l_name) != NULL) { 316 mutex_spin_exit(&tty_lock); 317 return (EEXIST); 318 } 319 320 ttyldisc_assign_legacy_number(disc); 321 LIST_INSERT_HEAD(&ttyldisc_list, disc, l_list); 322 323 mutex_spin_exit(&tty_lock); 324 325 return (0); 326 } 327 328 /* 329 * Remove a line discipline. 330 */ 331 int 332 ttyldisc_detach(struct linesw *disc) 333 { 334 #ifdef DIAGNOSTIC 335 struct linesw *ldisc = ttyldisc_lookup(disc->l_name); 336 337 KASSERT(ldisc != NULL); 338 KASSERT(ldisc == disc); 339 ttyldisc_release(ldisc); 340 #endif 341 342 mutex_spin_enter(&tty_lock); 343 344 if (TTYLDISC_ISINUSE(disc)) { 345 mutex_spin_exit(&tty_lock); 346 return (EBUSY); 347 } 348 349 LIST_REMOVE(disc, l_list); 350 351 mutex_spin_exit(&tty_lock); 352 353 return (0); 354 } 355 356 /* 357 * Return the default line discipline. 358 */ 359 struct linesw * 360 ttyldisc_default(void) 361 { 362 363 return (&termios_disc); 364 } 365