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
67int
68linux_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);
375out:
376 fd_putfile(SCARG(uap, fd));
377 return error;
378}
379