1 | /* $NetBSD: linux_termios.c,v 1.37 2013/12/27 16:58:50 njoly Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1995, 1998, 2008 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Frank van der Linden and Eric Haszlakiewicz. |
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 | #include <sys/cdefs.h> |
33 | __KERNEL_RCSID(0, "$NetBSD: linux_termios.c,v 1.37 2013/12/27 16:58:50 njoly Exp $" ); |
34 | |
35 | #if defined(_KERNEL_OPT) |
36 | #include "opt_ptm.h" |
37 | #endif |
38 | |
39 | #include <sys/param.h> |
40 | #include <sys/proc.h> |
41 | #include <sys/systm.h> |
42 | #include <sys/file.h> |
43 | #include <sys/filedesc.h> |
44 | #include <sys/ioctl.h> |
45 | #include <sys/mount.h> |
46 | #include <sys/termios.h> |
47 | #include <sys/kernel.h> |
48 | |
49 | #include <sys/syscallargs.h> |
50 | |
51 | #include <compat/linux/common/linux_types.h> |
52 | #include <compat/linux/common/linux_ioctl.h> |
53 | #include <compat/linux/common/linux_signal.h> |
54 | #include <compat/linux/common/linux_util.h> |
55 | #include <compat/linux/common/linux_termios.h> |
56 | #include <compat/linux/common/linux_ipc.h> |
57 | #include <compat/linux/common/linux_sem.h> |
58 | |
59 | #include <compat/linux/linux_syscallargs.h> |
60 | |
61 | #ifdef DEBUG_LINUX |
62 | #define DPRINTF(a) uprintf a |
63 | #else |
64 | #define DPRINTF(a) |
65 | #endif |
66 | |
67 | int |
68 | linux_ioctl_termios(struct lwp *l, const struct linux_sys_ioctl_args *uap, register_t *retval) |
69 | { |
70 | /* { |
71 | syscallarg(int) fd; |
72 | syscallarg(u_long) com; |
73 | syscallarg(void *) data; |
74 | } */ |
75 | file_t *fp; |
76 | u_long com; |
77 | struct linux_termio tmplt; |
78 | struct linux_termios tmplts; |
79 | struct termios tmpbts; |
80 | int idat; |
81 | struct sys_ioctl_args ia; |
82 | int error; |
83 | char tioclinux; |
84 | int (*bsdioctl)(file_t *, u_long, void *); |
85 | |
86 | if ((fp = fd_getfile(SCARG(uap, fd))) == NULL) |
87 | return (EBADF); |
88 | |
89 | if ((fp->f_flag & (FREAD | FWRITE)) == 0) { |
90 | error = EBADF; |
91 | goto out; |
92 | } |
93 | |
94 | bsdioctl = fp->f_ops->fo_ioctl; |
95 | com = SCARG(uap, com); |
96 | retval[0] = 0; |
97 | |
98 | switch (com) { |
99 | case LINUX_TCGETS: |
100 | error = (*bsdioctl)(fp, TIOCGETA, &tmpbts); |
101 | if (error) |
102 | goto out; |
103 | bsd_termios_to_linux_termios(&tmpbts, &tmplts); |
104 | error = copyout(&tmplts, SCARG(uap, data), sizeof tmplts); |
105 | goto out; |
106 | case LINUX_TCSETS: |
107 | case LINUX_TCSETSW: |
108 | case LINUX_TCSETSF: |
109 | /* |
110 | * First fill in all fields, so that we keep the current |
111 | * values for fields that Linux doesn't know about. |
112 | */ |
113 | error = (*bsdioctl)(fp, TIOCGETA, &tmpbts); |
114 | if (error) |
115 | goto out; |
116 | error = copyin(SCARG(uap, data), &tmplts, sizeof tmplts); |
117 | if (error) |
118 | goto out; |
119 | linux_termios_to_bsd_termios(&tmplts, &tmpbts); |
120 | switch (com) { |
121 | case LINUX_TCSETS: |
122 | com = TIOCSETA; |
123 | break; |
124 | case LINUX_TCSETSW: |
125 | com = TIOCSETAW; |
126 | break; |
127 | case LINUX_TCSETSF: |
128 | com = TIOCSETAF; |
129 | break; |
130 | } |
131 | error = (*bsdioctl)(fp, com, &tmpbts); |
132 | goto out; |
133 | case LINUX_TCGETA: |
134 | error = (*bsdioctl)(fp, TIOCGETA, &tmpbts); |
135 | if (error) |
136 | goto out; |
137 | bsd_termios_to_linux_termio(&tmpbts, &tmplt); |
138 | error = copyout(&tmplt, SCARG(uap, data), sizeof tmplt); |
139 | goto out; |
140 | case LINUX_TCSETA: |
141 | case LINUX_TCSETAW: |
142 | case LINUX_TCSETAF: |
143 | /* |
144 | * First fill in all fields, so that we keep the current |
145 | * values for fields that Linux doesn't know about. |
146 | */ |
147 | error = (*bsdioctl)(fp, TIOCGETA, &tmpbts); |
148 | if (error) |
149 | goto out; |
150 | error = copyin(SCARG(uap, data), &tmplt, sizeof tmplt); |
151 | if (error) |
152 | goto out; |
153 | linux_termio_to_bsd_termios(&tmplt, &tmpbts); |
154 | switch (com) { |
155 | case LINUX_TCSETA: |
156 | com = TIOCSETA; |
157 | break; |
158 | case LINUX_TCSETAW: |
159 | com = TIOCSETAW; |
160 | break; |
161 | case LINUX_TCSETAF: |
162 | com = TIOCSETAF; |
163 | break; |
164 | } |
165 | error = (*bsdioctl)(fp, com, &tmpbts); |
166 | goto out; |
167 | case LINUX_TCFLSH: |
168 | switch((u_long)SCARG(uap, data)) { |
169 | case 0: |
170 | idat = FREAD; |
171 | break; |
172 | case 1: |
173 | idat = FWRITE; |
174 | break; |
175 | case 2: |
176 | idat = 0; |
177 | break; |
178 | default: |
179 | error = EINVAL; |
180 | goto out; |
181 | } |
182 | error = (*bsdioctl)(fp, TIOCFLUSH, &idat); |
183 | goto out; |
184 | case LINUX_TIOCGETD: |
185 | error = (*bsdioctl)(fp, TIOCGETD, &idat); |
186 | if (error) |
187 | goto out; |
188 | switch (idat) { |
189 | case TTYDISC: |
190 | idat = LINUX_N_TTY; |
191 | break; |
192 | case SLIPDISC: |
193 | idat = LINUX_N_SLIP; |
194 | break; |
195 | case PPPDISC: |
196 | idat = LINUX_N_PPP; |
197 | break; |
198 | case STRIPDISC: |
199 | idat = LINUX_N_STRIP; |
200 | break; |
201 | /* |
202 | * Linux does not have the tablet line discipline. |
203 | */ |
204 | case TABLDISC: |
205 | default: |
206 | idat = -1; /* XXX What should this be? */ |
207 | break; |
208 | } |
209 | error = copyout(&idat, SCARG(uap, data), sizeof idat); |
210 | goto out; |
211 | case LINUX_TIOCSETD: |
212 | error = copyin(SCARG(uap, data), &idat, sizeof idat); |
213 | if (error) |
214 | goto out; |
215 | switch (idat) { |
216 | case LINUX_N_TTY: |
217 | idat = TTYDISC; |
218 | break; |
219 | case LINUX_N_SLIP: |
220 | idat = SLIPDISC; |
221 | break; |
222 | case LINUX_N_PPP: |
223 | idat = PPPDISC; |
224 | break; |
225 | case LINUX_N_STRIP: |
226 | idat = STRIPDISC; |
227 | break; |
228 | /* |
229 | * We can't handle the mouse line discipline Linux has. |
230 | */ |
231 | case LINUX_N_MOUSE: |
232 | case LINUX_N_AX25: |
233 | case LINUX_N_X25: |
234 | case LINUX_N_6PACK: |
235 | default: |
236 | error = EINVAL; |
237 | goto out; |
238 | } |
239 | error = (*bsdioctl)(fp, TIOCSETD, &idat); |
240 | goto out; |
241 | case LINUX_TIOCLINUX: |
242 | error = copyin(SCARG(uap, data), &tioclinux, sizeof tioclinux); |
243 | if (error != 0) |
244 | goto out; |
245 | switch (tioclinux) { |
246 | case LINUX_TIOCLINUX_KERNMSG: |
247 | /* |
248 | * XXX needed to not fail for some things. Could |
249 | * try to use TIOCCONS, but the char argument |
250 | * specifies the VT #, not an fd. |
251 | */ |
252 | error = 0; |
253 | goto out; |
254 | case LINUX_TIOCLINUX_COPY: |
255 | case LINUX_TIOCLINUX_PASTE: |
256 | case LINUX_TIOCLINUX_UNBLANK: |
257 | case LINUX_TIOCLINUX_LOADLUT: |
258 | case LINUX_TIOCLINUX_READSHIFT: |
259 | case LINUX_TIOCLINUX_READMOUSE: |
260 | case LINUX_TIOCLINUX_VESABLANK: |
261 | case LINUX_TIOCLINUX_CURCONS: /* could use VT_GETACTIVE */ |
262 | error = EINVAL; |
263 | goto out; |
264 | } |
265 | break; |
266 | case LINUX_TIOCGWINSZ: |
267 | SCARG(&ia, com) = TIOCGWINSZ; |
268 | break; |
269 | case LINUX_TIOCSWINSZ: |
270 | SCARG(&ia, com) = TIOCSWINSZ; |
271 | break; |
272 | case LINUX_TIOCGPGRP: |
273 | SCARG(&ia, com) = TIOCGPGRP; |
274 | break; |
275 | case LINUX_TIOCSPGRP: |
276 | SCARG(&ia, com) = TIOCSPGRP; |
277 | break; |
278 | case LINUX_FIONREAD: |
279 | SCARG(&ia, com) = FIONREAD; |
280 | break; |
281 | case LINUX_FIONBIO: |
282 | SCARG(&ia, com) = FIONBIO; |
283 | break; |
284 | case LINUX_FIOASYNC: |
285 | SCARG(&ia, com) = FIOASYNC; |
286 | break; |
287 | case LINUX_TIOCEXCL: |
288 | SCARG(&ia, com) = TIOCEXCL; |
289 | break; |
290 | case LINUX_TIOCNXCL: |
291 | SCARG(&ia, com) = TIOCNXCL; |
292 | break; |
293 | case LINUX_TIOCCONS: |
294 | SCARG(&ia, com) = TIOCCONS; |
295 | break; |
296 | case LINUX_TIOCNOTTY: |
297 | SCARG(&ia, com) = TIOCNOTTY; |
298 | break; |
299 | case LINUX_TCSBRK: |
300 | idat = (u_long)SCARG(uap, data); |
301 | if (idat != 0) |
302 | SCARG(&ia, com) = TIOCDRAIN; |
303 | else { |
304 | if ((error = (*bsdioctl)(fp, TIOCSBRK, NULL)) != 0) |
305 | goto out; |
306 | error = tsleep(&idat, PZERO | PCATCH, "linux_tcsbrk" , hz / 4); |
307 | if (error == EINTR || error == ERESTART) { |
308 | (void)(*bsdioctl)(fp, TIOCCBRK, NULL); |
309 | error = EINTR; |
310 | } else |
311 | error = (*bsdioctl)(fp, TIOCCBRK, NULL); |
312 | goto out; |
313 | } |
314 | break; |
315 | case LINUX_TIOCMGET: |
316 | SCARG(&ia, com) = TIOCMGET; |
317 | break; |
318 | case LINUX_TIOCMSET: |
319 | SCARG(&ia, com) = TIOCMSET; |
320 | break; |
321 | case LINUX_TIOCMBIC: |
322 | SCARG(&ia, com) = TIOCMBIC; |
323 | break; |
324 | case LINUX_TIOCMBIS: |
325 | SCARG(&ia, com) = TIOCMBIS; |
326 | break; |
327 | #ifdef LINUX_TIOCGPTN |
328 | case LINUX_TIOCGPTN: |
329 | #ifndef NO_DEV_PTM |
330 | { |
331 | struct ptmget ptm; |
332 | |
333 | error = (*bsdioctl)(fp, TIOCPTSNAME, &ptm); |
334 | if (error != 0) |
335 | goto out; |
336 | error = copyout(&ptm.sfd, SCARG(uap, data), |
337 | sizeof(ptm.sfd)); |
338 | goto out; |
339 | } |
340 | #endif /* NO_DEV_PTM */ |
341 | #endif /* LINUX_TIOCGPTN */ |
342 | #ifdef LINUX_TIOCSPTLCK |
343 | case LINUX_TIOCSPTLCK: |
344 | fd_putfile(SCARG(uap, fd)); |
345 | error = copyin(SCARG(uap, data), &idat, sizeof(idat)); |
346 | if (error) |
347 | return error; |
348 | DPRINTF(("TIOCSPTLCK %d\n" , idat)); |
349 | return 0; |
350 | #endif |
351 | case LINUX_TCXONC: |
352 | idat = (u_long)SCARG(uap, data); |
353 | switch (idat) { |
354 | case LINUX_TCOOFF: |
355 | SCARG(&ia, com) = TIOCSTOP; |
356 | break; |
357 | case LINUX_TCOON: |
358 | SCARG(&ia, com) = TIOCSTART; |
359 | break; |
360 | case LINUX_TCIOFF: |
361 | case LINUX_TCION: |
362 | default: |
363 | error = EINVAL; |
364 | goto out; |
365 | } |
366 | break; |
367 | default: |
368 | error = EINVAL; |
369 | goto out; |
370 | } |
371 | |
372 | SCARG(&ia, fd) = SCARG(uap, fd); |
373 | SCARG(&ia, data) = SCARG(uap, data); |
374 | error = sys_ioctl(curlwp, &ia, retval); |
375 | out: |
376 | fd_putfile(SCARG(uap, fd)); |
377 | return error; |
378 | } |
379 | |