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